On 04-May-2019 20:31, Liu Hao wrote:
在 2019/5/4 上午6:16, David Mathog 写道:

Thanks for the feedback, but I'm still nowhere near getting this to work.


Issues:


1.  Compiler warnings:

c_spawn_n.c: In function 'main':
c_spawn_n.c:47:13: warning: passing argument 3 of 'CreateThread' from
incompatible pointer type [-Wincompatible-pointer-types]
             RunCommand,             // thread function name
             ^~~~~~~~~~

`LPTHREAD_START_ROUTINE` is the type of the parameter of
`CreateThread()`, which a pointer-to-function. The proper return type
for a thread procedure is `DWORD` rather than this thing.

Changed the called routine to (see below):

DWORD  RunCommand(  void *lpParam  ){

but ...

gcc -g -O0 -o c_spawn_n c_spawn_n.c
c_spawn_n.c: In function 'main':
c_spawn_n.c:47:13: warning: passing argument 3 of 'CreateThread' from incompatible pointer type [-Wincompatible-pointer-types]
             RunCommand,             // thread function name
             ^~~~~~~~~~
In file included from C:/progs/msys32/mingw32/i686-w64-mingw32/include/winbase.h:29:0, from C:/progs/msys32/mingw32/i686-w64-mingw32/include/windows.h:70,
                 from c_spawn_n.c:7:
C:/progs/msys32/mingw32/i686-w64-mingw32/include/processthreadsapi.h:163:28: note: expected 'LPTHREAD_START_ROUTINE' but argument is of type 'DWORD (*)(void *) {aka long unsigned int (*)(void *)}' WINBASEAPI HANDLE WINAPI CreateThread (LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);
                            ^~~~~~~~~~~~
The processthreadsapi.h header line does indeed contain the type which the compiler
is warning about:

grep CreateThread C:/progs/msys32/mingw32/i686-w64-mingw32/include/processthreadsapi.h

WINBASEAPI HANDLE WINAPI CreateThread (LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);

I don't see how to reconcile this.


2.  when run procs is shown as -1, not sure how that happens.

Use `WaitForSingleObject()` to await **every** thread in a loop. Do not
use `WaitForMultipleObjects()` as it has a upper limit of number of
handles to wait for, and the return value is hard to handle properly (as
you already did that wrong).

You lost me. WaitForSingleObject() will only wait on a single handle at a time. There might be 20 subprocesses running, each with its own handle. A function is needed which will return when the first of those exits. Would that not be WaitForMultipleObjects()? Even if it will only work for lets say the first 16 processes, hopefully once those exit the invisible ones
would slide into view?  Or maybe it doesn't work that way?


Don't forget to `CloseHandle()` when a thread handle is out of interest.
The POSIX function `wait()` waits for a child process and frees its
resource, but on Windows you close its handle explicitly; wait functions
do not free resources of it.

OK, added that. For good measure set the handle pointer to NULL after closing it.


3.  cannot tell very well if this is actually starting multiple
processes or if it
is just running sequentially.
4.  there are now about about 100 cmd.exe processes showing up in
Windows Task Manager.

The `system` function passes its argument to the program designated by
the environment variable `COMSPEC` (which seems to have to be an
absolute path), which by default is `CMD.EXE` so you get a number of
instances.

If this is unacceptable you have to spawn child processes using
`CreateProcess()` and redirect standard streams yourself. There is a
nice document about this [1].

[1]
https://docs.microsoft.com/en-us/windows/desktop/ProcThread/creating-a-child-process-with-redirected-input-and-output


So something is hanging somewhere.  It was run both in  "MSYS2 Mingw 32"
and a W7
cmd shell.   These processes are not visible in "ps -ef" in the former.

`ps` only shows programs that run on the abstraction layer for POSIX of
MSYS2; native programs are out of its management.

5.  Pretty sure I'm not checking status right on the thread creation and
not
passing the system() exit status back correctly (or at all).
6.  During the test it says this a variable number of times:

  The process cannot access the file because it is being used by another
process.

Rewrote the example. The input file was simplified to commands3.txt and is now:

dir #c_spawn_n.c# > #c_spawn_n.c#.killme
dir accudate.1 > accudate.1.killme
dir accudate.c > accudate.c.killme

(which should run in either shell, those were just 3 files which were available
in the current directory.)

Rewrote the code to NOT check for running processes until all (3) were started.
Ran it like:

 ./c_spawn_n <commands3.txt
DEBUG firstempty 0 command >dir #c_spawn_n.c# > #c_spawn_n.c#.killme< len 40 procs 0 DEBUG firstempty 1 command >dir accudate.1 > accudate.1.killme< len 34 procs 1 DEBUG firstempty 2 command >dir accudate.c > accudate.c.killme< len 34 procs 2
DEBUG CLEANUP thread -1 exited with status 4200656l
DEBUG CLEANUP thread -1 exited with status 4200656l
DEBUG CLEANUP thread -1 exited with status 4200656l

It is checking for a returned NULL handle, and that isn't happening, so the threads
seemed to have started.

SOMETIMES it makes one or more killme files, sometimes it does not.
The commands are independent of each other, so unclear how they might
be interfering in this case.

Sometimes cmd.exe are left running as seen in the task manager. Sometimes not.

Tried various values for the return status from RunCommand, among them 0 and 10.
Didn't seem to make any difference.

Again, this is in a mingw32 shell, using that gcc.  Current code:


// c_spawn_n.c
// TEST program to run up to 20 commands in parallel as subprocesses.
// Read commands from stdin.
//
//

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <process.h>
#include <unistd.h>

// prototypes
DWORD WINAPI RunCommand( void *lpParam ); // runs thread

int     max_threads=20;
int     procs=0;                    // Number of processes running

int main(int argc, char *argv[]){
#define MAXSTRING 256
   char *command = malloc(MAXSTRING);
   char **ThrCommands = calloc(sizeof(char *), max_threads);
   HANDLE *ThrHandles = (HANDLE *) calloc(sizeof(HANDLE),max_threads);
   DWORD  *ThreadIds = (DWORD *) calloc(sizeof(DWORD),max_threads);
   int   firstempty=0;
   int   procs=0;
   DWORD retVal;
   while(1){
        if(NULL == fgets(command,MAXSTRING,stdin))break;

//DEBUG simple test, just run 3 commands in order before checking for exit
        if(0 && procs > 0){
            firstempty = WaitForMultipleObjects(
                 max_threads, ThrHandles, FALSE, INFINITE);
                procs--;
                GetExitCodeThread(ThrHandles[firstempty], &retVal);
            CloseHandle(ThrHandles[firstempty]);
            free(ThrCommands[firstempty]);
            ThrCommands[firstempty]=NULL;
fprintf(stderr,"DEBUG thread %d exited with status %ul\n",
firstempty,retVal);
        }
        //remove final character
        int clen=strlen(command);
        if(clen<=1)break;  // exit on an empty line
        command[strlen(command)-1]='\0';

fprintf(stderr,"DEBUG firstempty %d command >%s< len %d procs %d\n",
firstempty,command,strlen(command),procs);
        ThrCommands[firstempty] = strdup(command); // save it
        ThrHandles[firstempty] = CreateThread(
            NULL,                   // default security attributes
            0,                      // use default stack size
            RunCommand,             // thread function name
            ThrCommands[firstempty],// argument to thread function
            0,                      // use default creation flags
            &ThreadIds[firstempty]); // returns the thread identifier
        if(ThrHandles[firstempty] == NULL){
                fprintf(stderr,"DEBUG CreateThread failed\n");
        }
        procs++;
firstempty++; //DEBUG
    }
    // wait for threads to exit
    while(procs>0){
            firstempty = WaitForMultipleObjects(
                max_threads, ThrHandles, FALSE, INFINITE);
                procs--;
                GetExitCodeThread(ThrHandles[firstempty], &retVal);
            CloseHandle(ThrHandles[firstempty]);
            free(ThrCommands[firstempty]);
            ThrCommands[firstempty]=NULL;
fprintf(stderr,"DEBUG CLEANUP thread %d exited with status %ul\n",
firstempty,retVal);
    }
}

// run a command, return its exit status
DWORD WINAPI RunCommand(  void *lpParam  ){
        char *command = (char *) lpParam;

        int retval = system(command);
// output from next command never appears in console (DOS or MSYS)
//fprintf(stderr,"DEBUG RunCommand status %d with command %s\n",retval,command);
    return (0); //DEBUG
//    return (retval);
}



Thanks,


David Mathog
mat...@caltech.edu
Manager, Sequence Analysis Facility, Biology Division, Caltech


_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to