How to synchronize the execution of the parent / child process?

I would like to execute the child process and synchronize it (possibly with Mutex), without waiting for the child process to complete:

Parent:

program Project1; {$APPTYPE CONSOLE} uses Windows, ShellApi, SysUtils, Dialogs; procedure ShellExecEx(Wnd: HWND; const AExeFilename, AParams: string); const SEE_MASK_NOZONECHECKS = $00800000; SEE_MASK_WAITFORINPUTIDLE = $02000000; SEE_MASK_NOASYNC = $00000100; var Info: TShellExecuteInfo; begin FillChar(Info, SizeOf(Info), 0); Info.Wnd := Wnd; Info.cbSize := SizeOf(Info); Info.fMask := SEE_MASK_FLAG_NO_UI or SEE_MASK_NOZONECHECKS or SEE_MASK_NOASYNC //or SEE_MASK_WAITFORINPUTIDLE (works only with UI app ???) //or SEE_MASK_NO_CONSOLE //or SEE_MASK_NOCLOSEPROCESS ; Info.lpVerb := ''; Info.lpFile := PChar(AExeFilename); Info.lpParameters := PChar(AParams); Info.lpDirectory := PChar(ExtractFilePath(AExeFilename)); Info.nShow := SW_SHOWNORMAL; if not ShellExecuteEx(@Info) then RaiseLastOSError; CloseHandle(Info.hProcess); end; var Mutex: THandle = 0; Error: DWORD; begin OutputDebugString('Project1 : 1'); ShellExecEx(0, 'Project2.exe', ''); // synchronize repeat // attempt to create a named mutex Mutex := CreateMutex(nil, False, 'F141518A-E6E4-4BC0-86EB-828B1BC48DD1'); Error := GetLastError; if Mutex = 0 then RaiseLastOSError; CloseHandle(Mutex); until Error = ERROR_ALREADY_EXISTS; OutputDebugString('Project1 : 3'); end. 

Child:

 program Project2; {$APPTYPE CONSOLE} uses SysUtils, Windows, Dialogs; var Mutex: THandle = 0; begin OutputDebugString('Project2 : 2'); // attempt to create a named mutex and acquire ownership Mutex := CreateMutex(nil, True, 'F141518A-E6E4-4BC0-86EB-828B1BC48DD1'); if Mutex = 0 then RaiseLastOSError; // do something ReleaseMutex(Mutex); CloseHandle(Mutex); // <- at this point Program1.exe should exit the repeat loop ShowMessage('ok from Project2'); end. 

I expect to see the output:

 Project1 : 1 Project2 : 2 Project1 : 3 

The problem is that sometimes the parent (Project1.exe) does not exit the loop.
What am I doing wrong?

+7
source share
1 answer

You have a mutex race. You are hoping for the following sequence:

 child: create mutex parent: open mutex child: destroy mutex 

But what could happen

 child: create mutex child: destroy mutex parent: open mutex (fails because mutex is destroyed) 

I cannot fully understand what your ultimate goal is, but I have a suspicion that the event is actually what you are looking for.

In parent:

  • Create a named event.
  • Set an event without an alarm.
  • Create a child process.
  • Wait until the event is signaled.

The child has:

  • Do some processing.
  • Open a named event.
  • Set an event for signaling, thereby freeing the parent from his expectation.

At a very high level, the code you need will look like this:

Parent

 Event = CreateEvent(nil, True, False, EventName); //create it manual reset, set to non-signaled ShellExecEx(....); WaitForSingleObject(Event); 

Child

 Event = CreateEvent(nil, True, False, EventName); //do stuff SetEvent(Event); 

I did not enable error checking. I am sure you can add some. You may also find that the event wrapper class in SyncObjs more convenient.


Finally, your code has a busy cycle. This is almost never a solution to a problem. If you ever find yourself writing a busy cycle, you should take this as a signal of a bad design. The fact is that in your code, if it could be made to work, the parent process would burn 100% CPU usage while waiting for the child process.

+11
source

All Articles