0

I have a question regarding Win32 API process treatment.

I have two processes. Process 1, before starting, must run process 2, which waits for the same resource as process 1. It is a .txt file with some integer values. It means that Process1 should be started FIRST and run Process2. The Process1 MUST be completed after Process2. It should work as followig: 1.Process1 is created. 2.Process 1 is blocked. 3.Process 2 is created and excecuted. 4.Process 1 is unlocked and executed. 5.Process 2 ends. 6.Process 1 ends.

I searched here for a question similar to mine, I've found only the link below, where a SpinLock class is presented:

C++11 Implementation of Spinlock using <atomic>

The issue is to implement it correctly, I've deleted my incorrect implementations of SpinLock methods from main() function.

It was almost impossible to find any example of using this method in practice, hence I am asking this question to have a look on it:

#include <iostream>
#include <Windows.h>
#include <string>
#include <tchar.h>
#include <cstdlib>
#include <pthread.h>
#include <atomic>
using namespace std;

class SpinLock {
    atomic_flag locked = ATOMIC_FLAG_INIT ;
public:
    void lock() {
        while (locked.test_and_set(memory_order_acquire)) { ; }
    }
    void unlock() {
        locked.clear(memory_order_release);
    }
};

int main( int argc, TCHAR *argv[] )
{
    //process 1 generates N random values between 1 and 100,then saves it to txt file i argv[1] stores quanity of values, which will be saved to file
    STARTUPINFO si = {};
    si.cb = sizeof si;
    SpinLock SpinLockVar;
   PROCESS_INFORMATION pi = {};
   const TCHAR* target1 = _T("C:\\USERS\\Admin\\Documents\\File1.exe"); //process 1
   const TCHAR* target2 = _T("C:\\USERS\\Admin\\Documents\\File2.exe");
   //Process 1 , before starting generating values and saving them to file, runs Process2,which is awaiting for access to txt file (SPINLOCK ).
   //Process 1 is terminating after finishing Process 2

   if ( !CreateProcess(target1,GetCommandLine(), 0, FALSE, 0, 0, 0, 0, &si, &pi) )
    {
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    }
    else
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
        
        if ( PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0) ) // Good
            cout << "Request to terminate process has been sent!";

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
    if ( !CreateProcess(target2,0, 0, FALSE, 0, 0, 0, 0, &si, &pi) )
    {
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    }
    else
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
        /*
        if ( TerminateProcess(pi.hProcess, 0) ) // Evil
            cout << "Process terminated!";
        */
        if ( PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0) ) // Good
            cout << "Request to terminate process has been sent!";

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }

    cin.sync();
    cin.ignore();
    
  

 
    return 0;
}

UPDATE I have used mutex function for now, and it works partially - it has a mutex "spinlock" mechanism, although it sometimes displaying very strange. I have randomly the result which I expect and which is generating randomly after running my program - the first line in cmd is from thread which runs process2, the second line is result of process1

Please check my code :

#include <windows.h>
#include <stdio.h>
#include <pthread.h>
#include <tchar.h>
#include <mutex>
#include <iostream>
HANDLE hMutex;

DWORD ThreadProc1(LPVOID* arg)  
{  
       
      const TCHAR* target = _T("C:\\USERS\\Admin\\Documents\\File2.exe");
    PROCESS_INFORMATION pInfo;
    STARTUPINFO sInfo = { sizeof(pInfo) };
    BOOL res = CreateProcess(target, 0, 0, FALSE, 0, 0, 0, 0, &sInfo, &pInfo); //process2
    if (!res) return 1;
      
      WaitForSingleObject(pInfo.hThread, INFINITE);
    CloseHandle(pInfo.hThread);
    CloseHandle(pInfo.hProcess);
      return TRUE;  
}  

 
int main(void)  
{  
     PROCESS_INFORMATION pInfo;
    STARTUPINFO sInfo = { sizeof(pInfo) };
    const TCHAR* target = _T("C:\\USERS\\Admin\\Documents\\File1.exe");
      HANDLE hThreads;  
      DWORD threadID1; 

      
      hMutex=CreateMutex(NULL, FALSE, NULL); //create mutex(resources=1)
      WaitForSingleObject(hMutex, INFINITE); //process2 call WaitForSingleObject(hmutex) first to get mutex
      hThreads=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc1, &hMutex, 0, &threadID1); 
      WaitForSingleObject(hMutex,INFINITE);//process1 call WaitForSingleObject(hmutex) and block
      BOOL res = CreateProcess(target, GetCommandLine(), 0, FALSE, 0, 0, 0, 0, &sInfo, &pInfo);//process1
      if (!res) return 1;
      ReleaseMutex(hMutex);// process2 do file operations, and then release mutex
      WaitForSingleObject(hMutex,INFINITE);// process1 WaitForSingleObject(hmutex) unblock(resources -1),
      ReleaseMutex(hMutex);  // then release mutex(resources +1) 

      
      CloseHandle(hMutex);   
      WaitForSingleObject(hThreads,INFINITE); 
      CloseHandle(hThreads);    //process 1 closing thread after process 2 ends
     CloseHandle(pInfo.hProcess);
     CloseHandle(pInfo.hThread);
      return 0;
}  
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
wiki11ful
  • 29
  • 1
  • 8
  • 1
    Named mutex required so it is visible beyond the process boundary. Use CreateMutex(). – Hans Passant Aug 06 '20 at 17:46
  • FYI, posting a `WM_QUIT` message to `pi.dwThreadId` is useless since the process that owns that thread (represented by `pi.hProcess`) has already fully terminated by the time `WaitForSingleObject(pi.hProcess, INFINITE)` exits. The whole point of waiting on a process `HANDLE` is to detect when the process has terminated. – Remy Lebeau Aug 06 '20 at 19:28
  • You metion that process2 must be run before process1, but your code shows that you run process1 first, then exit and run process2. Can you simply change the order to solve the problem without using spinlock? Because the 2 processes are not running at the same time. – Drake Wu Aug 07 '20 at 02:02
  • Or you want to use the `dwShareMode` parameter of [`CreateFile`](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea)(in the 2 processes) to restrict the access of other processes. – Drake Wu Aug 07 '20 at 02:22
  • Actually I deleted all chages which I done with those processess because they were incorrect, that's why I have order like Process1,Process2 - the point is, that Process1, before staring, must execute Process 2. Im trying with ```Create Mutex``` at the moment.. – wiki11ful Aug 07 '20 at 10:37
  • I've put an update – wiki11ful Aug 07 '20 at 11:18
  • I tried now to use methods from library #include to apply spilock, when I apply it, the second process is blocked instead of first one, I am sure I am doing something wrong and cannot find similar examples to mine – wiki11ful Aug 07 '20 at 14:41
  • @Hans Passant I used function ```CreateMutex``` although I have no idea how to make it to make process 1 lock and run process 2 – wiki11ful Aug 08 '20 at 14:25
  • Your code does not even use mutex. To use mutex, 1. create mutex(resources=1) 2. process2 call `WaitForSingleObject(hmutex)` first to get mutex(resources -1) 3. process1 call `WaitForSingleObject(hmutex)` and block(resources=0), 4. process2 do file operations, and then release mutex(resources +1) 5. process1 `WaitForSingleObject(hmutex)` unblock(resources -1), then release mutex(resources +1) – Drake Wu Aug 11 '20 at 06:39
  • Thanks for answer, although I am not sure how to define resources mentioned in your example - I've read official documentation of ```CreateMutex``` on Microsoft page and based on example provided there. ```CreateMutex``` function does not have parameters which I can use as "resource" – wiki11ful Aug 12 '20 at 17:09
  • Based on link https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexa – wiki11ful Aug 12 '20 at 17:10
  • No need to define, just to illustrate the problem. The mutex resource is 1 after `CreateMutex `, and the call to `WaitForSingleObject` will make target resource -1. You didn't use the wait function for resource mutual exclusion in your code. Refer to [Using Mutex Objects](https://learn.microsoft.com/en-us/windows/win32/sync/using-mutex-objects), Each thread uses wait and release to achieve mutual exclusion. – Drake Wu Aug 13 '20 at 02:02
  • Can you share the reason that you must use the lock mechanism? In my opinion, you only need to suspend process1 when creating it, then start process2, wait for process2 to exit, and then resume process1, see my sample of `CREATE_SUSPENDED`. More simply, change the order of process execution: run process2, wait for process2 to exit, run process1, wait for process1 to exit, unless you need to execute part of the code in process1 before run process2. – Drake Wu Aug 13 '20 at 02:38
  • The reason of spinlock in that it is expected and obligatory from my C.S profesor – wiki11ful Aug 13 '20 at 13:45
  • Ok, I did something like below, although it did not provided results as it should: – wiki11ful Aug 13 '20 at 14:42
  • will upgrade my original question,sorry – wiki11ful Aug 13 '20 at 14:43

1 Answers1

0

First, I think you don't need to use mutex or Spinlock. You could create process1 with CREATE_SUSPENDED, create process2, wait for process2 to exit, then call ResumeThread(pi1.hThread) although there may be differences in step 4 and 5.

#include <windows.h>
#include <string>
#include <tchar.h>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int argc, TCHAR* argv[])
{

    STARTUPINFO si1 = { 0 }, si2 = { 0 };
    si1.cb = sizeof si1;
    si2.cb = sizeof si2;
    PROCESS_INFORMATION pi1 = { 0 }, pi2 = { 0 };

    const TCHAR* target1 = _T("C:\\Users\\drakew\\source\\repos\\Project4\\Debug\\Project4.exe");
    const TCHAR* target2 = _T("C:\\Users\\drakew\\source\\repos\\Project6\\Debug\\Project6.exe");


    if (!CreateProcess(target1, 0, 0, FALSE, 0, CREATE_SUSPENDED, 0, 0, &si1, &pi1))
    {
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    }
    else
    {
        printf("Process1 is created ...\n");
        printf("Process1 is blocked ...\n");
        if (!CreateProcess(target2, 0, 0, FALSE, 0, 0, 0, 0, &si2, &pi2))//Process 2 is created and excecuted ...
        {
            cerr << "CreateProcess failed (" << GetLastError() << ").\n";
        }
        else
        {
            WaitForSingleObject(pi2.hProcess, INFINITE);
            printf("Process 2 ends ...\n");
            CloseHandle(pi2.hProcess);
            CloseHandle(pi2.hThread);
        }
        ResumeThread(pi1.hThread); //Process 1 is unlocked and executed ...
        WaitForSingleObject(pi1.hProcess, INFINITE);
        printf("Process 1 ends ...\n");
        CloseHandle(pi1.hProcess);
        CloseHandle(pi1.hThread);
    }
    cin.sync();
    cin.ignore();

    return 0;
}

Then, If you want to block at the specific location of process1, then you have to modify process1 and process2 to keep synchronization.

I use event to implement lock.

Process1:

#include <Windows.h>
#include <iostream>
using namespace std;
int main()
{
    HANDLE hEvent1 = OpenEvent(EVENT_MODIFY_STATE| SYNCHRONIZE, FALSE, L"Global\\MyEvent1");
    HANDLE hEvent2 = OpenEvent(EVENT_MODIFY_STATE| SYNCHRONIZE, FALSE, L"Global\\MyEvent2");
    printf("process1: step1 ...\n");
    SetEvent(hEvent1);
    printf("process1: step2 ...\n");
    DWORD dwWaitResult = WaitForSingleObject(hEvent2, INFINITE);
    switch (dwWaitResult)
    {
    case WAIT_OBJECT_0:
        printf("process1: step4 ...\n");
        break;
    default:
        return FALSE;
    }
    CloseHandle(hEvent1);
    CloseHandle(hEvent2);
}

Process2:

#include <Windows.h>
#include <iostream>
using namespace std;
int main()
{
    HANDLE hEvent1 = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, L"Global\\MyEvent1");
    HANDLE hEvent2 = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, L"Global\\MyEvent2");

    DWORD dwWaitResult = WaitForSingleObject(hEvent1, INFINITE);
    switch (dwWaitResult)
    {
    case WAIT_OBJECT_0:
        printf("process2: step3 ...\n");
        SetEvent(hEvent2);
        break;
    default:
        return FALSE;
    }
    CloseHandle(hEvent1);
    CloseHandle(hEvent2);
    return 1;
}

Main Process:

#include <windows.h>
#include <string>
#include <tchar.h>
#include <cstdlib>
#include <mutex>
#include <iostream>
using namespace std;
int main(int argc, TCHAR* argv[])
{

    STARTUPINFO si1 = { 0 }, si2 = { 0 };
    si1.cb = sizeof si1;
    si2.cb = sizeof si2;
    PROCESS_INFORMATION pi1 = { 0 }, pi2 = { 0 };
    mutex mtx;
    HANDLE hEvent1 = CreateEvent(NULL, FALSE, FALSE, L"Global\\MyEvent1");
    HANDLE hEvent2 = CreateEvent(NULL, FALSE, FALSE, L"Global\\MyEvent2");

    const TCHAR* target1 = _T("C:\\path\\process1.exe");
    const TCHAR* target2 = _T("C:\\path\\process2.exe");


    if (!CreateProcess(target1, 0, 0, FALSE, 0, 0, 0, 0, &si1, &pi1))
    {
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    }
    else
    {
        if (!CreateProcess(target2, 0, 0, FALSE, 0, 0, 0, 0, &si2, &pi2))
        {
            cerr << "CreateProcess failed (" << GetLastError() << ").\n";
        }
        else
        {
            WaitForSingleObject(pi2.hProcess, INFINITE);
            printf("process2: step5 ...\n");
            CloseHandle(pi2.hProcess);
            CloseHandle(pi2.hThread);
        }

        WaitForSingleObject(pi1.hProcess, INFINITE);
        printf("process1: step6 ...\n");
        CloseHandle(pi1.hProcess);
        CloseHandle(pi1.hThread);
    }
    CloseHandle(hEvent1);
    CloseHandle(hEvent2);
    cin.sync();
    cin.ignore();
    return 0;
}

Result:

enter image description here

Drake Wu
  • 6,927
  • 1
  • 7
  • 30