Andrew Cook : server: Implement TerminateJobObject.

Alexandre Julliard julliard at wine.codeweavers.com
Tue Apr 7 10:11:28 CDT 2015


Module: wine
Branch: master
Commit: 1d359a5827159fb0572d598ed02054ff3d700f44
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=1d359a5827159fb0572d598ed02054ff3d700f44

Author: Andrew Cook <ariscop at gmail.com>
Date:   Fri Apr  3 17:19:45 2015 +1100

server: Implement TerminateJobObject.

---

 dlls/kernel32/tests/process.c  |  2 --
 dlls/ntdll/sync.c              | 15 +++++++++++++--
 include/wine/server_protocol.h | 19 ++++++++++++++++++-
 server/process.c               | 39 ++++++++++++++++++++++++++++++++++++++-
 server/process.h               |  1 +
 server/protocol.def            |  7 +++++++
 server/request.h               |  5 +++++
 server/trace.c                 |  9 +++++++++
 8 files changed, 91 insertions(+), 6 deletions(-)

diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c
index 11c9253..d981ec9 100644
--- a/dlls/kernel32/tests/process.c
+++ b/dlls/kernel32/tests/process.c
@@ -2255,13 +2255,11 @@ static void test_TerminateJobObject(void)
     ok(ret, "TerminateJobObject error %u\n", GetLastError());
 
     dwret = WaitForSingleObject(pi.hProcess, 1000);
-    todo_wine
     ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
     if (dwret == WAIT_TIMEOUT) TerminateProcess(pi.hProcess, 0);
 
     ret = GetExitCodeProcess(pi.hProcess, &dwret);
     ok(ret, "GetExitCodeProcess error %u\n", GetLastError());
-    todo_wine
     ok(dwret == 123 || broken(dwret == 0) /* randomly fails on Win 2000 / XP */,
        "wrong exitcode %u\n", dwret);
 
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 5db736e..04c6de30 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -613,8 +613,19 @@ NTSTATUS WINAPI NtOpenJobObject( PHANDLE handle, ACCESS_MASK access, const OBJEC
  */
 NTSTATUS WINAPI NtTerminateJobObject( HANDLE handle, NTSTATUS status )
 {
-    FIXME( "stub: %p %x\n", handle, status );
-    return STATUS_SUCCESS;
+    NTSTATUS ret;
+
+    TRACE( "(%p, %d)\n", handle, status );
+
+    SERVER_START_REQ( terminate_job )
+    {
+        req->handle = wine_server_obj_handle( handle );
+        req->status = status;
+        ret = wine_server_call( req );
+    }
+    SERVER_END_REQ;
+
+    return ret;
 }
 
 /******************************************************************************
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index de6302c..d27914a 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -5156,6 +5156,20 @@ struct set_job_completion_port_reply
 };
 
 
+
+struct terminate_job_request
+{
+    struct request_header __header;
+    obj_handle_t handle;
+    int          status;
+    char __pad_20[4];
+};
+struct terminate_job_reply
+{
+    struct reply_header __header;
+};
+
+
 enum request
 {
     REQ_new_process,
@@ -5419,6 +5433,7 @@ enum request
     REQ_process_in_job,
     REQ_set_job_limits,
     REQ_set_job_completion_port,
+    REQ_terminate_job,
     REQ_NB_REQUESTS
 };
 
@@ -5687,6 +5702,7 @@ union generic_request
     struct process_in_job_request process_in_job_request;
     struct set_job_limits_request set_job_limits_request;
     struct set_job_completion_port_request set_job_completion_port_request;
+    struct terminate_job_request terminate_job_request;
 };
 union generic_reply
 {
@@ -5953,8 +5969,9 @@ union generic_reply
     struct process_in_job_reply process_in_job_reply;
     struct set_job_limits_reply set_job_limits_reply;
     struct set_job_completion_port_reply set_job_completion_port_reply;
+    struct terminate_job_reply terminate_job_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 466
+#define SERVER_PROTOCOL_VERSION 467
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/process.c b/server/process.c
index eba47f7..d912739 100644
--- a/server/process.c
+++ b/server/process.c
@@ -64,6 +64,7 @@ static int process_signaled( struct object *obj, struct wait_queue_entry *entry
 static unsigned int process_map_access( struct object *obj, unsigned int access );
 static void process_poll_event( struct fd *fd, int event );
 static void process_destroy( struct object *obj );
+static void terminate_process( struct process *process, struct thread *skip, int exit_code );
 
 static const struct object_ops process_ops =
 {
@@ -147,6 +148,7 @@ struct job
     struct list process_list;      /* list of all processes */
     int num_processes;             /* count of running processes */
     unsigned int limit_flags;      /* limit flags */
+    int terminating;               /* job is terminating */
     struct completion *completion_port; /* associated completion port */
     apc_param_t completion_key;    /* key to send with completion messages */
 };
@@ -188,6 +190,7 @@ static struct job *create_job_object( struct directory *root, const struct unico
             list_init( &job->process_list );
             job->num_processes = 0;
             job->limit_flags = 0;
+            job->terminating = 0;
             job->completion_port = NULL;
             job->completion_key = 0;
         }
@@ -251,12 +254,35 @@ static void release_job_process( struct process *process )
     assert( job->num_processes );
     job->num_processes--;
 
-    add_job_completion( job, JOB_OBJECT_MSG_EXIT_PROCESS, get_process_id(process) );
+    if (!job->terminating)
+        add_job_completion( job, JOB_OBJECT_MSG_EXIT_PROCESS, get_process_id(process) );
 
     if (!job->num_processes)
         add_job_completion( job, JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, 0 );
 }
 
+static void terminate_job( struct job *job, int exit_code )
+{
+    /* don't report completion events for terminated processes */
+    job->terminating = 1;
+
+    for (;;)  /* restart from the beginning of the list every time */
+    {
+        struct process *process;
+
+        /* find the first process associated with this job and still running */
+        LIST_FOR_EACH_ENTRY( process, &job->process_list, struct process, job_entry )
+        {
+            if (process->running_threads) break;
+        }
+        if (&process->job_entry == &job->process_list) break;  /* no process found */
+        assert( process->job == job );
+        terminate_process( process, NULL, exit_code );
+    }
+
+    job->terminating = 0;
+}
+
 static void job_destroy( struct object *obj )
 {
     struct job *job = (struct job *)obj;
@@ -1543,6 +1569,17 @@ DECL_HANDLER(process_in_job)
     release_object( process );
 }
 
+/* terminate all processes associated with the job */
+DECL_HANDLER(terminate_job)
+{
+    struct job *job = get_job_obj( current->process, req->handle, JOB_OBJECT_TERMINATE );
+
+    if (!job) return;
+
+    terminate_job( job, req->status );
+    release_object( job );
+}
+
 /* update limits of the job object */
 DECL_HANDLER(set_job_limits)
 {
diff --git a/server/process.h b/server/process.h
index 58c313a..59625f0 100644
--- a/server/process.h
+++ b/server/process.h
@@ -26,6 +26,7 @@
 struct atom_table;
 struct handle_table;
 struct startup_info;
+struct job;
 
 /* process startup state */
 enum startup_state { STARTUP_IN_PROGRESS, STARTUP_DONE, STARTUP_ABORTED };
diff --git a/server/protocol.def b/server/protocol.def
index 625b3c9..54501e8 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3544,3 +3544,10 @@ enum coords_relative
     obj_handle_t port;            /* handle to the completion port */
     client_ptr_t key;             /* key to send with completion messages */
 @END
+
+
+/* Terminate all processes associated with the job  */
+ at REQ(terminate_job)
+    obj_handle_t handle;          /* handle to the job */
+    int          status;          /* process exit code */
+ at END
diff --git a/server/request.h b/server/request.h
index dd6e4e2..f94235f 100644
--- a/server/request.h
+++ b/server/request.h
@@ -367,6 +367,7 @@ DECL_HANDLER(assign_job);
 DECL_HANDLER(process_in_job);
 DECL_HANDLER(set_job_limits);
 DECL_HANDLER(set_job_completion_port);
+DECL_HANDLER(terminate_job);
 
 #ifdef WANT_REQUEST_HANDLERS
 
@@ -634,6 +635,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_process_in_job,
     (req_handler)req_set_job_limits,
     (req_handler)req_set_job_completion_port,
+    (req_handler)req_terminate_job,
 };
 
 C_ASSERT( sizeof(affinity_t) == 8 );
@@ -2233,6 +2235,9 @@ C_ASSERT( FIELD_OFFSET(struct set_job_completion_port_request, job) == 12 );
 C_ASSERT( FIELD_OFFSET(struct set_job_completion_port_request, port) == 16 );
 C_ASSERT( FIELD_OFFSET(struct set_job_completion_port_request, key) == 24 );
 C_ASSERT( sizeof(struct set_job_completion_port_request) == 32 );
+C_ASSERT( FIELD_OFFSET(struct terminate_job_request, handle) == 12 );
+C_ASSERT( FIELD_OFFSET(struct terminate_job_request, status) == 16 );
+C_ASSERT( sizeof(struct terminate_job_request) == 24 );
 
 #endif  /* WANT_REQUEST_HANDLERS */
 
diff --git a/server/trace.c b/server/trace.c
index 0e2e7e2..236c9ab 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -4133,6 +4133,12 @@ static void dump_set_job_completion_port_request( const struct set_job_completio
     dump_uint64( ", key=", &req->key );
 }
 
+static void dump_terminate_job_request( const struct terminate_job_request *req )
+{
+    fprintf( stderr, " handle=%04x", req->handle );
+    fprintf( stderr, ", status=%d", req->status );
+}
+
 static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_new_process_request,
     (dump_func)dump_get_new_process_info_request,
@@ -4395,6 +4401,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_process_in_job_request,
     (dump_func)dump_set_job_limits_request,
     (dump_func)dump_set_job_completion_port_request,
+    (dump_func)dump_terminate_job_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -4659,6 +4666,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
     NULL,
     NULL,
     NULL,
+    NULL,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -4923,6 +4931,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
     "process_in_job",
     "set_job_limits",
     "set_job_completion_port",
+    "terminate_job",
 };
 
 static const struct




More information about the wine-cvs mailing list