From 0d393cfac6d2d4908f40a09565eab989bc71dfab Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Thu, 22 Jan 2015 01:02:53 +0100
Subject: nvcuda: Properly wrap stream callbacks by forwarding them to a worker
 thread.

---
 dlls/nvcuda/Makefile.in |   1 +
 dlls/nvcuda/nvcuda.c    | 136 ++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 122 insertions(+), 15 deletions(-)

diff --git a/dlls/nvcuda/Makefile.in b/dlls/nvcuda/Makefile.in
index 0921ce64cae..ab8a5a29a7a 100644
--- a/dlls/nvcuda/Makefile.in
+++ b/dlls/nvcuda/Makefile.in
@@ -1,4 +1,5 @@
 MODULE    = nvcuda.dll
+EXTRALIBS = $(PTHREAD_LIBS)
 
 C_SRCS = \
 	internal.c \
diff --git a/dlls/nvcuda/nvcuda.c b/dlls/nvcuda/nvcuda.c
index 9fefc28fa76..8b356fd6f5d 100644
--- a/dlls/nvcuda/nvcuda.c
+++ b/dlls/nvcuda/nvcuda.c
@@ -21,11 +21,17 @@
 #include "wine/port.h"
 
 #include <stdarg.h>
+#include <assert.h>
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
 
 #include "windef.h"
 #include "winbase.h"
 #include "wine/library.h"
 #include "wine/debug.h"
+#include "wine/list.h"
 #include "wine/wgl.h"
 #include "cuda.h"
 #include "nvcuda.h"
@@ -39,6 +45,30 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(nvcuda);
 
+struct stream_callback_entry
+{
+    struct list entry;
+    enum
+    {
+        STREAM_CALLBACK_ABANDONED,
+        STREAM_CALLBACK_PENDING,
+        STREAM_CALLBACK_EXECUTED
+    } status;
+    void (WINAPI *callback)(CUstream hStream, CUresult status, void *userData);
+    struct
+    {
+        CUstream stream;
+        CUresult status;
+        void *userdata;
+    } args;
+};
+
+static struct list stream_callbacks            = LIST_INIT( stream_callbacks );
+static pthread_mutex_t stream_callback_mutex   = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  stream_callback_request = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t  stream_callback_reply   = PTHREAD_COND_INITIALIZER;
+LONG num_stream_callbacks;
+
 static CUresult (*pcuArray3DCreate)(CUarray *pHandle, const CUDA_ARRAY3D_DESCRIPTOR *pAllocateArray);
 static CUresult (*pcuArray3DCreate_v2)(CUarray *pHandle, const CUDA_ARRAY3D_DESCRIPTOR *pAllocateArray);
 static CUresult (*pcuArray3DGetDescriptor)(CUDA_ARRAY3D_DESCRIPTOR *pArrayDescriptor, CUarray hArray);
@@ -1793,40 +1823,116 @@ CUresult WINAPI wine_cuPointerSetAttribute(const void *value, CUpointer_attribut
     return pcuPointerSetAttribute(value, attribute, ptr);
 }
 
-struct stream_callback
+static DWORD WINAPI stream_callback_worker_thread(LPVOID parameter)
 {
-    void (WINAPI *callback)(CUstream hStream, CUresult status, void *userData);
-    void *userData;
-};
+    struct stream_callback_entry *wrapper;
+    struct list *ptr;
+    pthread_mutex_lock(&stream_callback_mutex);
+
+    for (;;)
+    {
+        while ((ptr = list_head(&stream_callbacks)))
+        {
+            wrapper = LIST_ENTRY(ptr, struct stream_callback_entry, entry);
+            list_remove(&wrapper->entry);
+
+            switch (wrapper->status)
+            {
+                case STREAM_CALLBACK_ABANDONED:
+                    free(wrapper);
+                    break;
+
+                case STREAM_CALLBACK_PENDING:
+                    pthread_mutex_unlock(&stream_callback_mutex);
+
+                    TRACE("calling stream callback %p(%p, %d, %p)\n", wrapper->callback,
+                          wrapper->args.stream, wrapper->args.status, wrapper->args.userdata);
+                    wrapper->callback(wrapper->args.stream, wrapper->args.status, wrapper->args.userdata);
+                    TRACE("stream callback %p returned\n", wrapper->callback);
+
+                    wrapper->status = STREAM_CALLBACK_EXECUTED;
+                    pthread_cond_broadcast(&stream_callback_reply);
+                    pthread_mutex_lock(&stream_callback_mutex);
+                    break;
+
+                default:
+                    assert(0); /* never reached */
+            }
+
+            if (!--num_stream_callbacks)
+                goto end;
+        }
+
+        pthread_cond_wait(&stream_callback_request, &stream_callback_mutex);
+    }
+
+end:
+    pthread_mutex_unlock(&stream_callback_mutex);
+    return 0;
+}
 
 static void stream_callback_wrapper(CUstream hStream, CUresult status, void *userData)
 {
-    struct stream_callback *wrapper = userData;
-    TRACE("(%p, %d, %p)\n", hStream, status, userData);
+    struct stream_callback_entry *wrapper = userData;
+    wrapper->status         = STREAM_CALLBACK_PENDING;
+    wrapper->args.stream    = hStream;
+    wrapper->args.status    = status;
+    pthread_mutex_lock(&stream_callback_mutex);
 
-    TRACE("calling stream callback %p(%p, %d, %p)\n", wrapper->callback, hStream, status, wrapper->userData);
-    wrapper->callback(hStream, status, wrapper->userData);
-    TRACE("stream callback %p returned\n", wrapper->callback);
+    list_add_tail(&stream_callbacks, &wrapper->entry);
+    pthread_cond_signal(&stream_callback_request);
+    while (wrapper->status == STREAM_CALLBACK_PENDING)
+        pthread_cond_wait(&stream_callback_reply, &stream_callback_mutex);
 
-    HeapFree( GetProcessHeap(), 0, wrapper );
+    pthread_mutex_unlock(&stream_callback_mutex);
+    free(wrapper);
 }
 
 CUresult WINAPI wine_cuStreamAddCallback(CUstream hStream, void *callback, void *userData, unsigned int flags)
 {
-    struct stream_callback *wrapper;
+    struct stream_callback_entry *wrapper;
     CUresult ret;
 
     TRACE("(%p, %p, %p, %u)\n", hStream, callback, userData, flags);
 
-    wrapper = HeapAlloc( GetProcessHeap(), 0, sizeof(*wrapper) );
+    wrapper = malloc(sizeof(*wrapper));
     if (!wrapper)
         return CUDA_ERROR_OUT_OF_MEMORY;
+    wrapper->callback       = callback;
+    wrapper->args.userdata  = userData;
 
-    wrapper->callback = callback;
-    wrapper->userData = userData;
+    /* spawn a new worker thread if necessary */
+    pthread_mutex_lock(&stream_callback_mutex);
+    if (!num_stream_callbacks++)
+    {
+        HANDLE thread = CreateThread(NULL, 0, stream_callback_worker_thread, NULL, 0, NULL);
+        if (!thread)
+        {
+            num_stream_callbacks--;
+            pthread_mutex_unlock(&stream_callback_mutex);
+            free(wrapper);
+            return CUDA_ERROR_OUT_OF_MEMORY; /* FIXME */
+        }
+        CloseHandle(thread);
+    }
+    pthread_mutex_unlock(&stream_callback_mutex);
 
     ret = pcuStreamAddCallback(hStream, stream_callback_wrapper, wrapper, flags);
-    if (ret) HeapFree( GetProcessHeap(), 0, wrapper );
+    if (ret)
+    {
+        pthread_mutex_lock(&stream_callback_mutex);
+        if (num_stream_callbacks == 1)
+        {
+            wrapper->status = STREAM_CALLBACK_ABANDONED;
+            list_add_tail(&stream_callbacks, &wrapper->entry);
+            pthread_cond_signal(&stream_callback_request);
+            wrapper = NULL;
+        }
+        else num_stream_callbacks--;
+        pthread_mutex_unlock(&stream_callback_mutex);
+        free(wrapper);
+    }
+
     return ret;
 }
 
-- 
2.11.0

