Ilan Aizelman WS Ilan Aizelman WS -4 years ago 99
C Question

bank, 4 atm machine input txt files, syncing information between them with semaphores

I have a bank thread, and 4 ATM machine .txt files.(atm_0_input_file.txt - atm_4_input_file.txt).

Every file is a thread, and a bank is a thread. when the files are read the program ends. the bank outputs the current state of the accounts (id,amount,pw).

Problem:

The sync isn't done well, I'm expecting an output like this:
enter image description here

And I'm currently getting (if using all the 4 files) just one time bank's output. Btw, if I use only one file or two files, I get 2 outputs of the bank. I'm trying to achieve atleast 3-4 outputs of the bank, I'm using usleep(100000) for every file and sleep(3) for the bank.

Also, I'm using system calls for reading the files, reading every byte and saving to a string, then parsing it byte by byte.

Well, here's the code. I'm running CentOS on a Virtual Machine so I can't debug those 750 lines of code.

I hope you can help me, I'm working on it more than 24 hours without a break and feeling helpless..

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>

#define BUF_SIZE 8192
sem_t sem_log;
sem_t sem_rc;
sem_t sem_db;
int rc;
int n, numOfFiles;

char* logName = "log.txt";
int logDescriptor;

typedef struct Account{
int id;
int password;
int balance;
struct Account* next;
struct Account* prev;
sem_t sem_rc;
sem_t sem_db;
int rc;
}*acc;

acc head ,SaveHead;

typedef struct fileATM{
char fileName[50];
int fileNum;
}*fileAtm;

void parseMessage(char *msg, char tempId[20],char tempPw[20],char tempAmount[20],char temp2Id[20])
{
int i = 0;
int j = 0, k = 0, p = 0, h = 0;
int count = 0;
while(msg[i] != '\0')
{
if(msg[i] == ' '){
count++;
}
i++;
switch(count){
case 1: {tempId[j] = msg[i]; j++; break;}
case 2: {tempPw[k] = msg[i]; k++; break;}
case 3: {tempAmount[p] = msg[i]; p++; break;}
case 4: {temp2Id[h] = msg[i]; h++; break;}
}
//i++;
}
strcat(tempId, "\0");
strcat(tempPw, "\0");
strcat(tempAmount, "\0");
strcat(temp2Id, "\0");
}

//*finish: Update log.txt and lock it
void WriteLog(char* msg){
sem_wait(&sem_log);
strcat(msg,"\n");
write(logDescriptor,msg,strlen(msg));
sem_post(&sem_log);
}

void beforeReadAcc(acc temp)
{
sem_wait(&temp->sem_rc);
temp->rc++; //inc # of readers
if(temp->rc==1)//sem_wait(&temp->sem_db);
sem_post(&temp->sem_rc);
}

void beforeRead()
{
sem_wait(&sem_rc);
rc++; //inc # of readers
if(rc==1)sem_wait(&sem_db);
sem_post(&sem_rc);
}

void afterRead()
{
sem_wait(&sem_rc);
rc--; //dec # of readers
if(rc==0)sem_post(&sem_db); /*last reader*/
sem_post(&sem_rc);
}

void afterReadAcc(acc temp)
{
sem_wait(&temp->sem_rc);
temp->rc--; //dec # of readers
if(temp->rc==0)sem_post(&temp->sem_db); /*last reader*/
sem_post(&temp->sem_rc);
}


int doesAccExists(int id)
{
//BLOCK DB
//char* msgLog;
acc temp = (acc)malloc(sizeof(struct Account));
temp = head;
beforeRead();
if(temp != NULL){
while(temp != NULL)
{
sem_wait(&(temp->sem_db));
if(temp->id == id){
//RELEASE DB TWICE
sem_post(&(temp->sem_db));
afterRead();
return 1;
}
sem_post(&(temp->sem_db));
temp = temp->next;
}
}

afterRead();
return 0;
}

void TransferAccount(fileAtm fileName, char* msg)
{
printf("\n Transfering to account... %s\n", msg);
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
int length = strlen(msg);
char msgLog[400];
int found = 0;
strcat(msg,"\0");
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, temp2Id, tempAmount);

acc tempDest = (acc)malloc(sizeof(struct Account));
acc temp2 = (acc)malloc(sizeof(struct Account));
temp2=SaveHead;
tempDest = temp2;
printf(" %d", doesAccExists(atoi(tempId)));
printf("%d %d",atoi(tempId),atoi(temp2Id) );
if((doesAccExists(atoi(tempId)) == 1) && (found=doesAccExists(atoi(temp2Id)) == 1)){
while(temp2 != NULL)
{
sem_wait(&(temp2->sem_db));
if(temp2->id == atoi(tempId))
{
if(temp2->password == atoi(tempPw))
{
if(temp2->balance > atoi(tempAmount)){

temp2->balance -= atoi(tempAmount);
//Find destination id
sem_post(&(temp2->sem_db));
while(tempDest != NULL)
{
sem_wait(&(tempDest->sem_db));
if(tempDest->id == atoi(temp2Id)){
tempDest->balance += atoi(tempAmount);
found = 1;
sem_post(&(tempDest->sem_db));
break;
}
sem_post(&(tempDest->sem_db));
tempDest = tempDest->next;
}
beforeReadAcc(temp2);
sprintf(msgLog, "<ATM ID: %d>: Transfer <%d> from account <%d> to account <%d> new balance is <%d> new target account balance is <%d>", fileName->fileNum, atoi(tempAmount),temp2->id, tempDest->id, temp2->balance,tempDest->balance);
afterReadAcc(temp2);
WriteLog(msgLog);
return;
}
}
else
{
sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed - password for id <%d> is incorrect", fileName->fileNum, atoi(tempId));
WriteLog(msgLog);
sem_post(&(temp2->sem_db));
return;
}
}
sem_post(&(temp2->sem_db));
temp2 = temp2->next;
}//end while
}
else
{
if (found == 0)
{ //Did not found destination account
sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist", fileName->fileNum,atoi(temp2Id));
WriteLog(msgLog); return;
}
sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist", fileName->fileNum,atoi(tempId));
WriteLog(msgLog);
return;
}
}


void CloseAccount(fileAtm fileName, char* msg)
{
printf("\n Closing account.. %s\n", msg);
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
int length = strlen(msg);
char msgLog[400];
strcat(msg,"\0");
int temp2Bal;
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, tempAmount, temp2Id);
acc temp2 = (acc)malloc(sizeof(struct Account));
acc temp3 = (acc)malloc(sizeof(struct Account));
sem_init(&(temp3->sem_db),0,1);
temp2=SaveHead;

printf(" %d", doesAccExists(atoi(tempId)));
if(doesAccExists(atoi(tempId)) == 1){
while(temp2 != NULL)
{
sem_wait(&(temp2->sem_db));
if(temp2->id == atoi(tempId))
{
if(temp2->password == atoi(tempPw))
{
beforeRead();
//If it's the only item'
//Lock DB
//sem_wait(&sem_db);
if(temp2->prev == NULL && temp2->next == NULL) {
//Lock and Release temp2,temp3
beforeReadAcc(temp2);
sem_wait(&(temp3->sem_db));
temp3->balance = temp2->balance;
sem_post(&(temp3->sem_db));

sem_post(&(temp2->sem_db));
afterReadAcc(temp2);
free(temp2);
//Lock and release SaveHead,Head
SaveHead=head=NULL;
}
//If it's head and there're more items on da list
else if(temp2->prev == NULL && temp2->next != NULL)
{
beforeReadAcc(temp2);
//sem_wait(&(SaveHead->sem_db));
SaveHead = temp2->next;
//sem_post(&(SaveHead->sem_db));

sem_wait(&(temp2->next->sem_db));
temp2->next->prev = NULL;
sem_post(&(temp2->next->sem_db));


sem_wait(&(temp3->sem_db));
temp3->balance = temp2->balance;
sem_post(&(temp3->sem_db));

sem_post(&(temp2->sem_db));
afterReadAcc(temp2);
free(temp2);
}
//Delete from middle in the list
else if(temp2->prev != NULL && temp2->next != NULL)
{
beforeReadAcc(temp2);
sem_wait(&(temp2->prev->sem_db));
temp2->prev->next = temp2->next;
sem_post(&(temp2->prev->sem_db));

sem_wait(&(temp2->next->sem_db));
temp2->next->prev = temp2->prev;
sem_post(&(temp2->next->sem_db));

sem_wait(&(temp3->sem_db));
temp3->balance = temp2->balance;
sem_post(&(temp3->sem_db));

sem_post(&(temp2->sem_db));
afterReadAcc(temp2);
free(temp2);
}
//Delete from the end
else if(temp2->prev != NULL && temp2->next == NULL)
{
beforeReadAcc(temp2);
sem_wait(&(temp2->prev->sem_db));
temp2->prev->next = NULL;
sem_post(&(temp2->prev->sem_db));

sem_wait(&(temp3->sem_db));
temp3->balance = temp2->balance;
sem_post(&(temp3->sem_db));

sem_post(&(temp2->sem_db));
afterReadAcc(temp2);
free(temp2);
}
//Release DB
//sem_post(&sem_db);
afterRead();
sprintf(msgLog, "<ATM ID: %d>: Account <%d> is now closed. Balance was <%d>", fileName->fileNum, atoi(tempId), temp3->balance);
WriteLog(msgLog); return;
}
else
{
sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed - password for id <%d> is incorrect", fileName->fileNum, atoi(tempId));
WriteLog(msgLog); return;
}
}
sem_post(&(temp2->sem_db));
temp2 = temp2->next;
}
}
else
{
sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist", fileName->fileNum,atoi(tempId));
WriteLog(msgLog);
return;
}
}


void depositAccount(fileAtm fileName, char* msg)
{
printf("\n Depositing to account.. %s\n", msg);
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
int length = strlen(msg);
char msgLog[400];
strcat(msg,"\0");
int SaveId;
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, tempAmount, temp2Id);

acc temp2 = (acc)malloc(sizeof(struct Account));
temp2=SaveHead;

printf(" %d", doesAccExists(atoi(tempId)));
if(doesAccExists(atoi(tempId)) == 1){
sem_wait(&(temp2->sem_db));
while(temp2 != NULL)
{
if(temp2->id == atoi(tempId))
{
if(temp2->password == atoi(tempPw))
{
//Block User's DB'

temp2->balance += atoi(tempAmount);
//Release User's DB'
sem_post(&(temp2->sem_db));
beforeReadAcc(temp2);
sprintf(msgLog, "<ATM ID: %d>: Account <%d> new balance is <%d> after <%d>$ was deposited", fileName->fileNum, temp2->id, temp2->balance, atoi(tempAmount));
afterReadAcc(temp2);
WriteLog(msgLog);
return;
}
else
{
sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed - password for id <%d> is incorrect", fileName->fileNum, temp2->id);
WriteLog(msgLog); return;
}
}
sem_post(&(temp2->sem_db));
temp2 = temp2->next;
}//end while
}
else
{
sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist", fileName->fileNum,atoi(tempId));
WriteLog(msgLog);
return;
}
}

void Withdrawl(fileAtm fileName, char* msg)
{
printf("\n Withdrawl from account.. %s\n", msg);
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
int length = strlen(msg);
char msgLog[400];
strcat(msg,"\0");
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, tempAmount, temp2Id);

acc temp2 = (acc)malloc(sizeof(struct Account));
temp2=SaveHead;

printf(" %d", doesAccExists(atoi(tempId)));
if(doesAccExists(atoi(tempId)) == 1){
sem_wait(&(temp2->sem_db));
while(temp2 != NULL)
{
if(temp2->id == atoi(tempId)){
if(temp2->password == atoi(tempPw)){
//Block User's DB'
if(temp2->balance > atoi(tempAmount))
{
temp2->balance -= atoi(tempAmount);
//Release User's DB'
sem_post(&(temp2->sem_db));
//Lock acc
beforeReadAcc(temp2);
sprintf(msgLog, "<ATM ID: %d>: Account <%d> new balance is <%d> after <%d>$ was Withdrawl", fileName->fileNum, temp2->id, temp2->balance, atoi(tempAmount));
//Release acc
afterReadAcc(temp2);
WriteLog(msgLog);
return;
}
else
{
sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed account id <%d> balance is lower than <%d> ", fileName->fileNum, temp2->id, atoi(tempAmount));
WriteLog(msgLog); return;
}
}
else
{
sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed - password for id <%d> is incorrect", fileName->fileNum, atoi(tempId));
WriteLog(msgLog); return;
}
}
sem_post(&(temp2->sem_db));
temp2 = temp2->next;
}
}
else
{
sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist", fileName->fileNum,atoi(tempId));
WriteLog(msgLog);
return;
}
}

void Balance(fileAtm fileName, char* msg){
printf("\n Balance from account.. %s\n", msg);
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
int length = strlen(msg);
char msgLog[400];
strcat(msg,"\0");
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, tempAmount, temp2Id);

acc temp2 = (acc)malloc(sizeof(struct Account));
temp2=SaveHead;

printf(" %d", doesAccExists(atoi(tempId)));
if(doesAccExists(atoi(tempId)) == 1){
while(temp2 != NULL)
{
if(temp2->id == atoi(tempId))
{
if(temp2->password == atoi(tempPw))
{
//lock user
beforeReadAcc(temp2);
sprintf(msgLog, "<ATM ID: %d>: Account <%d> new balance is <%d> ", fileName->fileNum, temp2->id, temp2->balance);
//release user
afterReadAcc(temp2);
WriteLog(msgLog); return;
}
else
{
sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed - password for account id <%d> is incorrect", fileName->fileNum, temp2->id);
WriteLog(msgLog); return;
}
}
temp2 = temp2->next;
}
}
else
{
sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist", fileName->fileNum,temp2->id);
WriteLog(msgLog);
return;
}
}


void openNewAccount(fileAtm fileName, char* msg){
printf("\n Opening account.. %s", msg);
int i = 0;
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
acc temp = (acc)malloc(sizeof(struct Account));
int length = strlen(msg);
char msgLog[400];
strcat(msg,"\0");
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, tempAmount, temp2Id);

//Translate id,pw,amount from string to int
temp->id = atoi(tempId);
temp->password = atoi(tempPw);
temp->balance = atoi(tempAmount);

acc temp2 = (acc)malloc(sizeof(struct Account));
acc tempTest = (acc)malloc(sizeof(struct Account));
temp2 = head;
tempTest = head;

//Search if id exists
beforeRead();
while(temp2 != NULL)
{
sem_wait(&temp2->sem_db);
if(temp2->id == temp->id){
sem_post(&temp2->sem_db);
sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed - account with the same id exists", fileName->fileNum);
WriteLog(msgLog);
return;
}
else{
sem_post(&temp2->sem_db);
temp2 = temp2->next;
}
}
afterRead();

//Start opening account - lock db
sem_wait(&sem_db);

//Inititalize list, append its head
if(head == NULL){
//List is empty

head = (acc) malloc(sizeof(struct Account));
sem_init(&(head->sem_db), 0, 1);
sem_init(&(head->sem_rc), 0, 1);

//Lock user's DB'
sem_wait(&(head->sem_db));
head->id = temp->id;
head->password = temp->password;
head->balance = temp->balance;
head->prev = NULL;
head->next = NULL;
head->rc = 0;

SaveHead = head;

//Release user's DB'
sem_post(&(head->sem_db));
printf("\n**Account: id %d, pw %d, amount %d\n", head->id, head->password, head->balance);
//Update log
sem_wait(&head->sem_db);
sprintf(msgLog, "<ATM ID: %d> New account id is <%d> with password <%d> and initial balance <%d>,", fileName->fileNum, head->id, head->password, head->balance);
sem_post(&head->sem_db);
}
else
{
//List is not empty
//Lock user's DB'
temp->rc = 0;
sem_init(&(temp->sem_db), 0, 1);
sem_init(&(temp->sem_rc), 0, 1);
temp->prev=NULL;
temp->next=NULL;
//sem_post(&(temp->sem_db));
//Find tail

while(head->next != NULL)
{
sem_wait(&(head->sem_db));
tempTest = head;
head=head->next;
sem_post(&(tempTest->sem_db));
}

sem_wait(&(temp->sem_db));
temp->prev=head;
sem_post(&(temp->sem_db));

sem_wait(&(head->sem_db));
head->next=temp;
sem_post(&(head->sem_db));

head=SaveHead;

sem_wait(&temp->sem_db);
printf("\n***Account: id %d, pw %d, amount %d\n", temp->id, temp->password, temp->balance);
sem_post(&temp->sem_db);

//Update log
sem_wait(&temp->sem_db);
sprintf(msgLog, "<ATM ID: %d> New account id is <%d> with password <%d> and initial balance <%d>,", fileName->fileNum, temp->id, temp->password, temp-> balance);
sem_post(&temp->sem_db);
}

WriteLog(msgLog);

//Account created - Release DB
sem_post(&sem_db);


}

//*Opens file for every ATM
void* openFile(void* args){
//test:

//To add later: while(true) { sleep(100); do next file's line }
//Open file
fileAtm getFile = (fileAtm) args;
printf("\n %s ", getFile->fileName);
int ret_in, in1,file;
char buffer1[BUF_SIZE];
char myLetter;
int count = 0;
int i = 0, j = 0,k;

char msg[30][30];
file = open(getFile->fileName,O_RDONLY,0644);
//Test:
// printf("\n %s ", getFile->fileName);
//msg = "test";

if((ret_in = read (file, &buffer1, BUF_SIZE)) > 0)
{
for(i=0,k=0; i<ret_in; i++,k++)
{
if(buffer1[i]=='\n')
{j++; count++; k=0;i++;}

msg[j][k] = buffer1[i];

}
}
printf("# of lines: %d %c", count, msg[1][0]);
//Here we call the relevant function of the msg

for(j=0; j<=count;j++)
{
switch (msg[j][0]){

case 'O': {openNewAccount(getFile,&(msg[j][0]));
// printf("\ntest :%c ",msg[j][0]);
break;}
case 'D': {printf("\ntest :%c ",msg[j][0]);depositAccount(getFile, &(msg[j][0])); break; }
case 'W': {printf("\ntest :%c ",msg[j][0]);Withdrawl(getFile, &(msg[j][0])); break; }
case 'B': {printf("\ntest :%c ",msg[j][0]);Balance(getFile, &(msg[j][0])); break;}
case 'Q': {printf("\ntest :%c ",msg[j][0]);CloseAccount(getFile, &(msg[j][0])); break;}
case 'T': {printf("\ntest :%c ",msg[j][0]);TransferAccount(getFile, &(msg[j][0])); break; }
}
}
usleep(100000);
numOfFiles++;
close(file);

}


void* bankLoop(void* nothing)
{

int totalAmount = 0;
acc temp = (acc)malloc(sizeof(struct Account));
acc temp2 = (acc)malloc(sizeof(struct Account));
while(1)
{
sleep(1);
temp = head;
beforeRead();
printf("\n*******************************\n");
printf("\nBank status: \n");
while(temp != NULL)
{
beforeReadAcc(temp);
printf("\nAccount: %d, Account password: %d ,Balance: %d$",temp->id, temp->password, temp->balance);
totalAmount+= temp->balance;
temp2 = temp;
temp = temp->next;
afterReadAcc(temp2);
}

//Print and Reset bank's balance to zero
printf("\nBank's balance: %d\n", totalAmount);
if(totalAmount != 0) totalAmount = 0;
printf("\n*********************************\n");
afterRead();
if(numOfFiles == n)
break;
}


}


int main(void)
{
int i,a;
pthread_t bank;
char fileName[50];
head = NULL;
numOfFiles = 0;
//Init semaphores
sem_init(&sem_log, 0, 1);
sem_init(&sem_rc,0,1);
sem_init(&sem_db,0,1);

printf("Please enter the number of ATMs you want: \n");
scanf("%d", &n);

//Open log file in background
logDescriptor = open(logName,O_WRONLY|O_CREAT,0644);


//Create bank thread
pthread_create( &bank, NULL, bankLoop, NULL);

//Initialization FILES threads
fileAtm files = (fileAtm)malloc(n*sizeof(struct fileATM));
//Files initialization
for(i = 0; i < n; i++){
sprintf(fileName, "ATM_%d_input_file.txt", i);
strcpy((files[i]).fileName,fileName);
(files[i]).fileNum = i;
}
//Testing:
for(i=0; i<n;i++)
printf("\n%s %d", (files[i]).fileName, (files[i]).fileNum);

//Threads (ATM) initialization
pthread_t* atmThreads = (pthread_t*)malloc(n*sizeof(pthread_t));


printf("test\n"); //TEST: check msg

//Create ATMs threads
for(i = 0; i < n; i++){
pthread_create ( &atmThreads[i] , NULL , openFile , (void*)&files[i]);
}

//Join bank thread
pthread_join(bank,NULL);

//Join ATM threads
for(i=0; i < n; i++)
pthread_join(atmThreads[i],NULL);


//scanf("%d",&a);
}


sorry for those 750 lines of code, I hope someone will have the time and be courage enough to check it out.


ATM_0_input_file.txt


O 1111 1111 123
O 4444 4444 598
T 4444 4444 2222 61
W 1111 1234 50
D 4444 4444 50
D 2222 0300 50
D 2222 0000 500
Q 1111 1111
Q 4444 4444
W 2234 2345 50
O 1212 4444 433
O 1432 4444 23
O 1165 4444 2
O 0986 4444
Q 1212 4444
Q 1432 4444
Q 1165 4444
Q 0986 4444



ATM_1_input_file.txt


O 1234 1234 60
D 1234 1234 50
W 1234 1234 20
D 3333 1234 20
T 1234 0000 2244 20
W 1234 1234 900



ATM_2_input_file.txt


D 1234 0000 60
O 3456 1234 20
D 3333 1234 20
T 1234 0000 2244 20
W 1234 0000 900
D 3456 1234 999
D 1234 0000 9
T 3456 1234 2244 203
Q 3333 1234



ATM_3_input_file.txt


O 7777 7777 1766
O 4444 4444 598
T 4444 4444 7777 5
T 7777 7777 4444 500
D 1111 1234 50
O 2222 1234 50
Q 1111 1111
Q 4444 4444
W 2234 2345 50


Edit: Fixed the warning. added the .txt files

Answer Source

I know it's tagged , but since you spammed it in Lounge<C++>, let me humor you with C++:

Live On Coliru

#include <algorithm>
#include <functional>
#include <atomic>
#include <fstream>
#include <iostream>
#include <list>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

// not thread-aware:
struct Bank {
    struct Account {
        int id;
        int password;
        int balance;

        Account(int id, int password = 0, int balance = 0)
            : id(id), password(password), balance(balance) { }

        bool operator==(Account const& other) const { return id == other.id; }
    };

    void remove(int id) {
        auto& acc = get_unverified(id);
        accounts.remove(acc);
    }

    void add(Account const& acc) {
        if (std::count(accounts.begin(), accounts.end(), acc.id))
            throw std::runtime_error("account with the same id exists"); // TODO include id?
        accounts.push_back(acc);
    }

    Account& get_unverified(int id) {
        auto it = std::find(accounts.begin(), accounts.end(), id);
        if (it != accounts.end())
            return *it;
        throw std::runtime_error("Account " + std::to_string(id) + " doesn't exist");
    }

    Account& get(int id, int password) {
        auto& acc = get_unverified(id);
        if (acc.password != password)
            throw std::runtime_error("Password for id <" + std::to_string(id) + "> is incorrect");
        return acc;
    }

    void status() const {
        std::cout << "*******************************\n";
        std::cout << "Bank status: \n";

        int totalAmount = 0;
        for (auto const &acc : accounts) {
            std::cout << "Account: " << acc.id << ", Account password: " << acc.password << ", Balance: " << acc.balance
                << "$\n";
            totalAmount += acc.balance;
        }

        // Print and Reset bank's balance to zero
        std::cout << "Bank's balance: " << totalAmount << "\n";
        std::cout << "*******************************\n";
    }

  private:
    std::list<Account> accounts;
};

// something to make access guarded:
template <typename T>
struct Locking {
    template <typename Op>
    void transactional(Op&& op) {
        std::lock_guard<std::mutex> lk(_mx);
        std::forward<Op>(op)(_value);
    }

  private:
    std::mutex _mx;
    T _value;
};

static void bankLoop(Locking<Bank>& safe, std::atomic_bool& keepRunning) {
    while (keepRunning) {
        std::this_thread::sleep_for(std::chrono::seconds(1));

        safe.transactional(std::mem_fn(&Bank::status));
    }
}

struct Log {
    Log() : ofs("log.txt") { } // TODO fix mode/fail on existing?

    void Write(std::string const &msg) {
        std::lock_guard<std::mutex> lk(_mx);
        ofs << msg << "\n";
    }

  private:
    std::mutex _mx;
    std::ofstream ofs;
} logDescriptor;

struct Atm {
    Locking<Bank>& safe;
    int const fileNum;

    void process(std::string const& fileName) {
        std::ifstream file(fileName);

        std::string line;
        while (std::getline(file, line)) {
            // totally made up this feature:
            if (line.empty()) {
                std::this_thread::sleep_for(std::chrono::milliseconds(200));
                continue;
            }

            switch (line[0]) {
                case 'O': openNewAccount(line); break;
                case 'D': depositAccount(line); break;
                case 'W': Withdrawl(line); break;
                case 'B': Balance(line); break;
                case 'Q': CloseAccount(line); break;
                case 'T': TransferAccount(line); break;
            }
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        file.close();
    }

  private:

    void TransferAccount(std::string const& msg) {
        std::cout << "Transfering to account... " << msg << "\n";

        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                auto& source = bank.get(cmd.id, cmd.pw);
                auto& target = bank.get_unverified(cmd.id2);

                source.balance -= cmd.amount;
                target.balance += cmd.amount;

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "Transfer <" << cmd.amount << "> "
                    << "from account <" << cmd.id << "> "
                    << "to account <" << cmd.id2 << "> "
                    << "new balance is <" << source.balance << "> "
                    << "new target account balance is <" << target.balance << ">";
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

    void CloseAccount(std::string const& msg) {
        std::cout << "Closing account... " << msg << "\n";
        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                auto& acc = bank.get(cmd.id, cmd.pw);
                auto const balance = acc.balance;

                bank.remove(acc.id);

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "Account <" << cmd.id << "> is now closed. "
                    << "Balance was <" << balance << ">";
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

    void depositAccount(std::string const& msg) {
        std::cout << "Depositing to account.. " << msg << "\n";
        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                auto& acc = bank.get(cmd.id, cmd.pw);
                acc.balance += cmd.amount;

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "Account <" << cmd.id << "> "
                    << "new balance is <" << acc.balance << "> "
                    << "after <" << cmd.amount << "> was deposited";
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

    void Withdrawl(std::string const& msg) {
        std::cout << "Withdrawl from account.. " << msg << "\n";
        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                auto& acc = bank.get(cmd.id, cmd.pw);
                acc.balance -= cmd.amount;

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "Account <" << cmd.id << "> "
                    << "new balance is <" << acc.balance << "> "
                    << "after <" << cmd.amount << "> was Withdrawl";
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

    void Balance(std::string const& msg) {
        std::cout << "Balance from account.. " << msg << "\n";
        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                auto& acc = bank.get(cmd.id, cmd.pw);

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "Account <" << cmd.id << "> "
                    << "new balance is <" << acc.balance << ">";
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

    void openNewAccount(std::string const& msg) {
        std::cout << "Opening account... " << msg << "\n";
        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                Bank::Account const acc(cmd.id, cmd.pw, cmd.amount);
                bank.add(acc);

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "New account id is <" << acc.id << "> with passoword <" << acc.password << "> and initial balance <" << acc.balance << ">,"; // FIXME trailing comma
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

  private:
    struct Cmd { int id=-1, pw=-1, amount=0, id2=-1; };
    static Cmd parseCommand(std::string const& msg) {
        Cmd result;
        char discard; // absorbs command character
        std::istringstream iss(msg);

        if (!(iss >> discard >> result.id >> result.pw))
            throw std::runtime_error("the command message is invalid");

        iss >> result.amount >> result.id2;

        return result;
    }
};

int main() {
    std::cout << "Please enter the number of ATMs you want: ";
    int n = 0;
    if (!(std::cin >> n))
        throw std::runtime_error("Input failed");

    // Create bank thread
    Locking<Bank> bank;
    std::atomic_bool keepRunning{true};
    std::thread bankThread(&bankLoop, std::ref(bank), std::ref(keepRunning));

    std::list<std::thread> atmThreads;
    for (int i = 0; i < n; i++) {
        atmThreads.emplace_back([&bank, i] {
            Atm atm { bank, i };
            atm.process("ATM_" + std::to_string(i) + "_input_file.txt");
        });
    }

    // Join ATM threads
    for (auto &atm : atmThreads)
        atm.join();

    // Join bank thread
    keepRunning = false;
    bankThread.join();
}

Let's test it with 1 ATM file

O 123 8888 10
O 123 8888 10
W 123 8888 5
B 123 8888
B 123 7777

O 234 9999 20
D 234 9999 50
B 234 9999

W 123 8888 15
T 234 9999 30  123
Q 234 9999
Q 234 9999
B 123 what

It prints

Please enter the number of ATMs you want: 1
Opening account... O 123 8888 10
Opening account... O 123 8888 10
Withdrawl from account.. W 123 8888 5
Balance from account.. B 123 8888
Balance from account.. B 123 7777
Opening account... O 234 9999 20
Depositing to account.. D 234 9999 50
Balance from account.. B 234 9999
Withdrawl from account.. W 123 8888 15
Transfering to account... T 234 9999 30  123
Closing account... Q 234 9999
Closing account... Q 234 9999
Balance from account.. B 123 what
*******************************
Bank status: 
Account: 123, Account password: 8888, Balance: 20$
Bank's balance: 20
*******************************

And log.txt ends up like:

<ATM ID: 0>: New account id is <123> with passoword <8888> and initial balance <10>,
Error <ATM ID: 0>: Your transaction failed  - account with the same id exists
<ATM ID: 0>: Account <123> new balance is <5> after <5> was Withdrawl
<ATM ID: 0>: Account <123> new balance is <5>
Error <ATM ID: 0>: Your transaction failed  - Password for id <123> is incorrect
<ATM ID: 0>: New account id is <234> with passoword <9999> and initial balance <20>,
<ATM ID: 0>: Account <234> new balance is <70> after <50> was deposited
<ATM ID: 0>: Account <234> new balance is <70>
<ATM ID: 0>: Account <123> new balance is <-10> after <15> was Withdrawl
<ATM ID: 0>: Transfer <30> from account <234> to account <123> new balance is <40> new target account balance is <20>
<ATM ID: 0>: Account <234> is now closed. Balance was <40>
Error <ATM ID: 0>: Your transaction failed  - Account 234 doesn't exist
Error <ATM ID: 0>: Your transaction failed  - the command message is invalid
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download