0

I need to write a program that create process multithreaded which is creating another process multithreaded and the problem that I having is there are some point time in the running of the two processes there are overlapping each other in printing to terminal.

for example :

//father process
printf("I'm father process\n");

//child process
fprintf(stderr,"I'm child process\n");

the outcome :

I'I'mm fachtheril proprcesscoess

is there anyway I can make sure that will not happen ?

the father and the child processes are connected with anonymous pipe if it's help.


add editing :

my main thread in the father process creating the vessels (threads) that each one have it's own id and announce this vessel is starting to work. then the thread itself need to use the pipe and sent his id to the child process. when the child process gets the id though the pipe he announce that the id arrived. but before that the child process initialize other thread that called crane with different purpose that also announce when they are created.

*each thread has it's own semaphore. *after each announce the thread enter to sleep between 5-3000 milliseconds.

so the problem it's happing only in the beginning when the main thread of the father process start creating the vessel and the main thread in the child process creating the cranes, there printing is overlapping or start one sentence stop start another one and return to the first one. though all the printing have a /n and command in one line.

I will try to add minimal reproducible example of my code:

father process:

main thread:

    /* create Input the pipe  */
if (!CreatePipe(&InputReadHandle, &InputWriteHandle, &sa, 0)) {
    fprintf(stderr, "Main::Create Pipe Failed @ father\n");
    return 1;
}
/* create Output the pipe */
if (!CreatePipe(&OutputReadHandle, &OutputWriteHandle, &sa, 0)) {
    fprintf(stderr, "Main::Create Pipe Failed @ father\n");
    return 1;
}

/* establish the START_INFO structure for the child process */
GetStartupInfo(&si);
si.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);

/* redirect the standard input/output to the read end of the pipe */
si.hStdOutput = OutputWriteHandle;
si.hStdInput = InputReadHandle;
si.dwFlags = STARTF_USESTDHANDLES;

/* we do not want the child to inherit the write end of the pipe */
SetHandleInformation(InputWriteHandle, HANDLE_FLAG_INHERIT, 0);


wcscpy(ProcessName, L"..\\..\\child\\Debug\\child.exe");


/* create the child process */
if (!CreateProcess(NULL,
    ProcessName,
    NULL,
    NULL,
    TRUE, /* inherit handles */
    0,
    NULL,
    NULL,
    &si,
    &pi))
{
    fprintf(stderr, "Main::Process Creation Failed @ father\n");
    return -1;
}


/* father now wants to write to the pipe */
if (!WriteFile(InputWriteHandle, &numOfVessels, BUFFER_SIZE, &written, NULL))
    fprintf(stderr, "Main::Error writing to pipe\n");

. . .

// Create all vessel Threads. Report if Error occurred!
for (int i = 0; i < numOfVessels; i++)
{
    vesselsID[i] = i+1;
    vesselsSem[i] = CreateSemaphore(NULL, 0, 1, NULL);
    if (vesselsSem[i] == NULL)
    {
        fprintf(stderr, "Main::Unexpected Error in Vessles Semaphore %d Creation\n", i);
        return FALSE;
    }
    vesselsArr[i] = CreateThread(NULL, 0, Vessel, &vesselsID[i], 0, &ThreadId);
    if (vesselsArr[i] == NULL) {
        fprintf(stderr,"Main::Unexpected Error in Thread %d Creation\n", i);
        exit(1);
    }
}

//wait to all thread(vessel) to finish.
WaitForMultipleObjects(numOfVessels, vesselsArr, TRUE, INFINITE);

vessel thread :

DWORD WINAPI Vessel(PVOID Param)
{
int id = *(int*)Param;

printf("%s Vessel %d - starts sailing @ father\n", getTime(),id);

Sleep(random());//Simulate a process Sailing

//sent to child .
    // make sure only one vessel at the time enter to the canal.
WaitForSingleObject(mutex, INFINITE);

printf( "%s Vessel %d - senting to child @father\n", getTime(), id);

Sleep(random());//Simulate a process  

//send the id vessel to child port through pipe.
if (!WriteFile(InputWriteHandle, &id, BUFFER_SIZE, &written, NULL))
    fprintf(stderr, "Error writing to pipe @ father\n");

// the vessel has been sent and can be release .
if (!ReleaseMutex(mutex))
{
    fprintf(stderr, " Unexpected error mutex.V()\n");
}
}

child process: main thread:

    for (int i = 0; i < numOfCrane; i++)
{
    adtArr[i].craneID = i + 1;

    craneSem[i] = CreateSemaphore(NULL, 0, 1, NULL);
    if (craneSem[i] == NULL)
    {
        fprintf(stderr, "Main::Unexpected Error in Vessles Semaphore %d Creation @child\n", i);
        return FALSE;
    }


    craneArr[i] = CreateThread(NULL, 0, Crane, &adtArr[i].craneID, 0, &ThreadId);
    if (craneArr[i] == NULL) {
        fprintf(stderr, "main::Unexpected Error in Thread %d Creation @child \n", i);
        exit(1);
    }
    adtArr[i].cargo = 0;
    adtArr[i].vesselID = 0;

    
    fprintf(stderr, "%s Crane %d created @child  \n", getTime(), adtArr[i].craneID);
}

. . .

//read the vessel from pipe
for (int i = 0; i < numOfVessels; i++)
    {   
        //if readfile is empty then it's wait.
        if (ReadFile(ReadHandle, buffer, BUFFER_SIZE, &read, NULL))
        {
            vesselsID[(*buffer) - 1] = (*buffer);
            vesselsSem[(*buffer) - 1] = CreateSemaphore(NULL, 0, 1, NULL);
            if (vesselsSem[(*buffer) - 1] == NULL)
            {
                fprintf(stderr, "Main::Unexpected Error in Vessles Semaphore %d Creation\n", (*buffer));
                return FALSE;
            }
            vesselsArr[(*buffer) - 1] = CreateThread(NULL, 0, Vessel, &vesselsID[(*buffer) - 1], 0, &ThreadId);
            if (vesselsArr[(*buffer) - 1] == NULL) {
                fprintf(stderr, "main::Unexpected Error in Thread %d Creation \n", (*buffer));
                exit(1);
            }
            barrier[i] = (*buffer); // need to write abinormal behavier

            
            fprintf(stderr, "%s Vessel %d - arrirved @ child \n", getTime(), *buffer);
            Sleep(random());
        }
    }

I hope I did well to explain myself.

  • Related: https://stackoverflow.com/q/467938 – Robert Harvey Jul 29 '21 at 20:00
  • 2
    Note that `stderr` and `stdio` are two different file handles, so it doesn't necessarily surprise me that there's no coordination between your two outputs. It will likely do what you expect if you use the same file handle for both processes. – Robert Harvey Jul 29 '21 at 20:02
  • but if I try to use in **stdio** in the child process it's not printing at all to the console – Aviv Sar shalom Jul 29 '21 at 20:17
  • @RobertHarvey: Using the “same” file handle in different processes will not do anything to coordinate between them. – Eric Postpischil Jul 29 '21 at 20:25
  • @EricPostpischil: Ah, so it's coordinated between threads in the same process. – Robert Harvey Jul 29 '21 at 20:38
  • 2
    The output you show seems unlikely from the calls you show. By default, `printf` to a terminal is generally line-buffered. The individual characters would not be sent a few at a time, even if you passed them a few at a time over several `printf` calls. They would be sent all at once when a new-line was seen or the buffer was full. So I suspect there is more to this problem than you have shown or described in the post. That said, to coordinate output from two processes, you need to coordinate between them, using some form of interprocess communication. What has your class studied recently? – Eric Postpischil Jul 29 '21 at 20:52
  • 1
    Please show us an [MCVE]. Your sample output is missing a d. – Jonathan Leffler Jul 29 '21 at 22:26
  • Your program demonstrates a race condition. https://searchstorage.techtarget.com/definition/race-condition – Jim Rogers Jul 29 '21 at 23:38
  • @EricPostpischil this is a concluding task in operating systems which we learned creating processes multithreaded using mutex and semaphores. at first I thought to create mutex to the terminal witch be shared of the two processes but I never done that and I don't know how. – Aviv Sar shalom Jul 30 '21 at 07:14
  • You could try adding some container that will store whole strings(for example linked list), and prints them one by one(could be a part of one thread or even completely new thread) – Lidbey Jul 30 '21 at 07:30
  • @JonathanLeffler hey I tried to add minimal reproducible example – Aviv Sar shalom Jul 30 '21 at 09:17
  • @AvivSarshalom: There is nothing particular about a mutex “to the terminal.” You simply create a plain mutex and use it to synchronize between the two processes. It is a mutex “to the terminal” simply because that is what the processes use it for. Each process should write to the terminal only while it holds the lock. Also, each process should flush the output when it wants it sent to the terminal. Otherwise, it may be held in a buffer. Flush the output using `fflush(stdout)` or, if writing to standard output going to a terminal, include a new-line character in the string being printed. – Eric Postpischil Jul 30 '21 at 11:13
  • @EricPostpischil: **There is nothing particular about a mutex “to the terminal.” You simply create a plain mutex and use it to synchronize between the two processes. It is a mutex “to the terminal” simply because that is what the processes use it for. Each process should write to the terminal only while it holds the lock.** how can I create a shared mutex between the two processes? I tried to use `fflush(stdout)` not didn't help and every string ha a new-line character . – Aviv Sar shalom Jul 30 '21 at 14:35
  • You don't even need a mutex. Use the anonymous pipe that is connecting the two and pass a token back and forth. – William Pursell Jul 30 '21 at 17:30
  • @WilliamPursell how do I do that? – Aviv Sar shalom Jul 31 '21 at 09:05
  • If the pipe is not being used for any other purpose, then you just pass a single byte back and forth. Start out by giving the parent permission to write to the tty. This means that the child will block on a read from the pipe before it attempts to write to the tty, but the parent doesn't. After the parent writes to the tty, it writes a single byte into the pipe, so the child's read unblocks. From that point forward, each process blocks on a read from the pipe before writing to the tty, and writes a single byte after each write to the tty. – William Pursell Jul 31 '21 at 13:37

1 Answers1

-1

The simpliest solution would be to add a newline character '\n'.

//father process
printf("I'm father process\n");

//child process
fprintf(stderr,"I'm child process\n");

This prevents the output of yours, but it does not prevent which comes first, the output of the parent or that of the child. If you want that behaviour synchronized, you need a bit more sophisticated synchronization (like shared memory and mutexes).

Erdal Küçük
  • 4,810
  • 1
  • 6
  • 11