Nikolay Sivov : services: Defer service delete until all handles are closed.

Alexandre Julliard julliard at winehq.org
Thu Jun 12 15:51:44 CDT 2014


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

Author: Nikolay Sivov <nsivov at codeweavers.com>
Date:   Wed Jun  4 23:54:17 2014 +0400

services: Defer service delete until all handles are closed.

---

 dlls/advapi32/service.c       |    2 ++
 dlls/advapi32/tests/service.c |   17 ++++++-----------
 programs/services/rpc.c       |   13 +++++++------
 programs/services/services.c  |    9 ++++++++-
 programs/services/services.h  |   10 ++++++++--
 5 files changed, 31 insertions(+), 20 deletions(-)

diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c
index 0047031..6bfdda6 100644
--- a/dlls/advapi32/service.c
+++ b/dlls/advapi32/service.c
@@ -1087,6 +1087,8 @@ BOOL WINAPI DeleteService( SC_HANDLE hService )
 {
     DWORD err;
 
+    TRACE("%p\n", hService);
+
     __TRY
     {
         err = svcctl_DeleteService(hService);
diff --git a/dlls/advapi32/tests/service.c b/dlls/advapi32/tests/service.c
index 4ebae1c..beb589d 100644
--- a/dlls/advapi32/tests/service.c
+++ b/dlls/advapi32/tests/service.c
@@ -187,7 +187,7 @@ static void test_open_svc(void)
 
 static void test_create_delete_svc(void)
 {
-    SC_HANDLE scm_handle, svc_handle1;
+    SC_HANDLE scm_handle, svc_handle1, svc_handle2;
     CHAR username[UNLEN + 1], domain[MAX_PATH];
     DWORD user_size = UNLEN + 1;
     CHAR account[UNLEN + 3];
@@ -412,6 +412,11 @@ static void test_create_delete_svc(void)
     ret = DeleteService(svc_handle1);
     ok(ret, "Expected success, got error %u\n", GetLastError());
 
+    /* Service is marked for delete, but handle is still open. Try to open service again. */
+    svc_handle2 = OpenServiceA(scm_handle, servicename, GENERIC_READ);
+    ok(svc_handle2 != NULL, "got %p, error %u\n", svc_handle2, GetLastError());
+    CloseServiceHandle(svc_handle2);
+
     CloseServiceHandle(svc_handle1);
     CloseServiceHandle(scm_handle);
 
@@ -2341,19 +2346,9 @@ static void test_refcount(void)
     svc_handle5 = CreateServiceA(scm_handle, servicename, NULL, GENERIC_ALL,
                                  SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
                                  SERVICE_DISABLED, 0, pathname, NULL, NULL, NULL, NULL, NULL);
-    todo_wine
-    {
     ok(!svc_handle5, "Expected failure\n");
     ok(GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE,
        "Expected ERROR_SERVICE_MARKED_FOR_DELETE, got %d\n", GetLastError());
-    }
-
-    /* FIXME: Remove this when Wine is fixed */
-    if (svc_handle5)
-    {
-        DeleteService(svc_handle5);
-        CloseServiceHandle(svc_handle5);
-    }
 
     /* Close all the handles to the service and try again */
     ret = CloseServiceHandle(svc_handle4);
diff --git a/programs/services/rpc.c b/programs/services/rpc.c
index 166e2b7..3955e09 100644
--- a/programs/services/rpc.c
+++ b/programs/services/rpc.c
@@ -481,8 +481,8 @@ DWORD __cdecl svcctl_CreateServiceW(
     DWORD dwPasswordSize,
     SC_RPC_HANDLE *phService)
 {
+    struct service_entry *entry, *found;
     struct sc_manager_handle *manager;
-    struct service_entry *entry;
     DWORD err;
 
     WINE_TRACE("(%s, %s, 0x%x, %s)\n", wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpDisplayName), dwDesiredAccess, wine_dbgstr_w(lpBinaryPathName));
@@ -533,11 +533,14 @@ DWORD __cdecl svcctl_CreateServiceW(
 
     scmdatabase_lock_exclusive(manager->db);
 
-    if (scmdatabase_find_service(manager->db, lpServiceName))
+    if ((found = scmdatabase_find_service(manager->db, lpServiceName)))
     {
+        service_lock_exclusive(found);
+        err = is_marked_for_delete(found) ? ERROR_SERVICE_MARKED_FOR_DELETE : ERROR_SERVICE_EXISTS;
+        service_unlock(found);
         scmdatabase_unlock(manager->db);
         free_service_entry(entry);
-        return ERROR_SERVICE_EXISTS;
+        return err;
     }
 
     if (scmdatabase_find_service_by_displayname(manager->db, get_display_name(entry)))
@@ -568,16 +571,14 @@ DWORD __cdecl svcctl_DeleteService(
     if ((err = validate_service_handle(hService, DELETE, &service)) != ERROR_SUCCESS)
         return err;
 
-    scmdatabase_lock_exclusive(service->service_entry->db);
     service_lock_exclusive(service->service_entry);
 
     if (!is_marked_for_delete(service->service_entry))
-        err = scmdatabase_remove_service(service->service_entry->db, service->service_entry);
+        err = mark_for_delete(service->service_entry);
     else
         err = ERROR_SERVICE_MARKED_FOR_DELETE;
 
     service_unlock(service->service_entry);
-    scmdatabase_unlock(service->service_entry->db);
 
     return err;
 }
diff --git a/programs/services/services.c b/programs/services/services.c
index 2fe3806..632d92a 100644
--- a/programs/services/services.c
+++ b/programs/services/services.c
@@ -245,7 +245,7 @@ DWORD scmdatabase_add_service(struct scmdatabase *db, struct service_entry *serv
     return ERROR_SUCCESS;
 }
 
-DWORD scmdatabase_remove_service(struct scmdatabase *db, struct service_entry *service)
+static DWORD scmdatabase_remove_service(struct scmdatabase *db, struct service_entry *service)
 {
     int err;
 
@@ -422,7 +422,14 @@ struct service_entry *scmdatabase_find_service_by_displayname(struct scmdatabase
 void release_service(struct service_entry *service)
 {
     if (InterlockedDecrement(&service->ref_count) == 0 && is_marked_for_delete(service))
+    {
+        scmdatabase_lock_exclusive(service->db);
+        service_lock_exclusive(service);
+        scmdatabase_remove_service(service->db, service);
+        service_unlock(service);
+        scmdatabase_unlock(service->db);
         free_service_entry(service);
+    }
 }
 
 static DWORD scmdatabase_create(struct scmdatabase **db)
diff --git a/programs/services/services.h b/programs/services/services.h
index 3e36141..88a84ad 100644
--- a/programs/services/services.h
+++ b/programs/services/services.h
@@ -48,6 +48,7 @@ struct service_entry
     HANDLE control_pipe;
     HANDLE overlapped_event;
     HANDLE status_changed_event;
+    BOOL marked_for_delete;
 };
 
 extern struct scmdatabase *active_database;
@@ -57,7 +58,6 @@ extern struct scmdatabase *active_database;
 struct service_entry *scmdatabase_find_service(struct scmdatabase *db, LPCWSTR name);
 struct service_entry *scmdatabase_find_service_by_displayname(struct scmdatabase *db, LPCWSTR name);
 DWORD scmdatabase_add_service(struct scmdatabase *db, struct service_entry *entry);
-DWORD scmdatabase_remove_service(struct scmdatabase *db, struct service_entry *entry);
 
 DWORD scmdatabase_lock_startup(struct scmdatabase *db);
 void scmdatabase_unlock_startup(struct scmdatabase *db);
@@ -106,7 +106,13 @@ static inline LPCWSTR get_display_name(struct service_entry *service)
 
 static inline BOOL is_marked_for_delete(struct service_entry *service)
 {
-    return service->entry.next == NULL;
+    return service->marked_for_delete;
+}
+
+static inline DWORD mark_for_delete(struct service_entry *service)
+{
+    service->marked_for_delete = TRUE;
+    return ERROR_SUCCESS;
 }
 
 #endif /*WINE_PROGRAMS_UTILS_H_*/




More information about the wine-cvs mailing list