This patch avoids simultaneous services startup race and fixes bug 44904.
This version of the patch adds a timeout parameter to scmdatabase_lock_startup() to make the approach more generic.
Please feel free to choose either old or new version of the patch.
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- programs/services/rpc.c | 4 ++-- programs/services/services.c | 15 ++++++++++++--- programs/services/services.h | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/programs/services/rpc.c b/programs/services/rpc.c index 5ecd6601a5..efbcd7645b 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -1212,7 +1212,7 @@ DWORD __cdecl svcctl_StartServiceW( if (service->service_entry->config.dwStartType == SERVICE_DISABLED) return ERROR_SERVICE_DISABLED;
- if (!scmdatabase_lock_startup(service->service_entry->db)) + if (!scmdatabase_lock_startup(service->service_entry->db, 3000)) return ERROR_SERVICE_DATABASE_LOCKED;
err = service_start(service->service_entry, dwNumServiceArgs, lpServiceArgVectors); @@ -1374,7 +1374,7 @@ DWORD __cdecl svcctl_LockServiceDatabase( if ((err = validate_scm_handle(hSCManager, SC_MANAGER_LOCK, &manager)) != ERROR_SUCCESS) return err;
- if (!scmdatabase_lock_startup(manager->db)) + if (!scmdatabase_lock_startup(manager->db, 0)) return ERROR_SERVICE_DATABASE_LOCKED;
lock = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sc_lock)); diff --git a/programs/services/services.c b/programs/services/services.c index f7c47b5bc2..eba98d6ee8 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -361,7 +361,7 @@ static void scmdatabase_autostart_services(struct scmdatabase *db)
scmdatabase_unlock(db); qsort(services_list, size, sizeof(services_list[0]), compare_tags); - while (!scmdatabase_lock_startup(db)) Sleep(10); + scmdatabase_lock_startup(db, INFINITE);
for (i = 0; i < size; i++) { @@ -619,9 +619,18 @@ static DWORD scmdatabase_load_services(struct scmdatabase *db) return ERROR_SUCCESS; }
-BOOL scmdatabase_lock_startup(struct scmdatabase *db) +BOOL scmdatabase_lock_startup(struct scmdatabase *db, int timeout) { - return !InterlockedCompareExchange(&db->service_start_lock, TRUE, FALSE); + while (InterlockedCompareExchange(&db->service_start_lock, TRUE, FALSE)) + { + if (timeout != INFINITE) + { + timeout -= 10; + if (timeout <= 0) return FALSE; + } + Sleep(10); + } + return TRUE; }
void scmdatabase_unlock_startup(struct scmdatabase *db) diff --git a/programs/services/services.h b/programs/services/services.h index fd19ed32f4..5a79c35778 100644 --- a/programs/services/services.h +++ b/programs/services/services.h @@ -77,7 +77,7 @@ struct service_entry *scmdatabase_find_service(struct scmdatabase *db, LPCWSTR n struct service_entry *scmdatabase_find_service_by_displayname(struct scmdatabase *db, LPCWSTR name); DWORD scmdatabase_add_service(struct scmdatabase *db, struct service_entry *entry);
-BOOL scmdatabase_lock_startup(struct scmdatabase *db); +BOOL scmdatabase_lock_startup(struct scmdatabase *db, int timeout); void scmdatabase_unlock_startup(struct scmdatabase *db);
void scmdatabase_lock(struct scmdatabase *db);