2 * Copyright 2015 Andrew Eikum for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "wine/list.h"
29 static pthread_key_t wine_gst_key
;
31 void mark_wine_thread(void)
33 /* set it to non-NULL to indicate that this is a Wine thread */
34 pthread_setspecific(wine_gst_key
, &wine_gst_key
);
37 static BOOL
is_wine_thread(void)
39 return pthread_getspecific(wine_gst_key
) != NULL
;
42 static pthread_mutex_t cb_list_lock
= PTHREAD_MUTEX_INITIALIZER
;
43 static pthread_cond_t cb_list_cond
= PTHREAD_COND_INITIALIZER
;
44 static struct list cb_list
= LIST_INIT(cb_list
);
46 static void CALLBACK
perform_cb(TP_CALLBACK_INSTANCE
*instance
, void *user
)
48 struct cb_data
*cbdata
= user
;
50 if (cbdata
->type
< GSTDEMUX_MAX
)
51 perform_cb_gstdemux(cbdata
);
53 pthread_mutex_lock(&cbdata
->lock
);
55 pthread_cond_broadcast(&cbdata
->cond
);
56 pthread_mutex_unlock(&cbdata
->lock
);
59 static DWORD WINAPI
dispatch_thread(void *user
)
61 struct cb_data
*cbdata
;
63 CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
65 pthread_mutex_lock(&cb_list_lock
);
69 while (list_empty(&cb_list
)) pthread_cond_wait(&cb_list_cond
, &cb_list_lock
);
71 cbdata
= LIST_ENTRY(list_head(&cb_list
), struct cb_data
, entry
);
72 list_remove(&cbdata
->entry
);
73 TrySubmitThreadpoolCallback(&perform_cb
, cbdata
, NULL
);
76 pthread_mutex_unlock(&cb_list_lock
);
83 void start_dispatch_thread(void)
85 pthread_key_create(&wine_gst_key
, NULL
);
86 CloseHandle(CreateThread(NULL
, 0, &dispatch_thread
, NULL
, 0, NULL
));
89 /* gstreamer calls our callbacks from threads that Wine did not create. Some
90 * callbacks execute code which requires Wine to have created the thread
91 * (critical sections, debug logging, dshow client code). Since gstreamer can't
92 * provide an API to override its thread creation, we have to intercept all
93 * callbacks in code which avoids the Wine thread requirement, and then
94 * dispatch those callbacks on a thread that is known to be created by Wine.
96 * This thread must not run any code that depends on the Wine TEB!
99 static void call_cb(struct cb_data
*cbdata
)
101 pthread_mutex_init(&cbdata
->lock
, NULL
);
102 pthread_cond_init(&cbdata
->cond
, NULL
);
103 cbdata
->finished
= 0;
105 if(is_wine_thread()){
106 /* The thread which triggered gstreamer to call this callback may
107 * already hold a critical section. If so, executing the callback on a
108 * worker thread can cause a deadlock. If we are already on a Wine
109 * thread, then there is no need to run this callback on a worker
110 * thread anyway, which avoids the deadlock issue. */
111 perform_cb(NULL
, cbdata
);
113 pthread_cond_destroy(&cbdata
->cond
);
114 pthread_mutex_destroy(&cbdata
->lock
);
119 pthread_mutex_lock(&cb_list_lock
);
121 list_add_tail(&cb_list
, &cbdata
->entry
);
122 pthread_cond_broadcast(&cb_list_cond
);
124 pthread_mutex_lock(&cbdata
->lock
);
126 pthread_mutex_unlock(&cb_list_lock
);
128 while(!cbdata
->finished
)
129 pthread_cond_wait(&cbdata
->cond
, &cbdata
->lock
);
131 pthread_mutex_unlock(&cbdata
->lock
);
133 pthread_cond_destroy(&cbdata
->cond
);
134 pthread_mutex_destroy(&cbdata
->lock
);
137 GstBusSyncReply
watch_bus_wrapper(GstBus
*bus
, GstMessage
*msg
, gpointer user
)
139 struct cb_data cbdata
= { WATCH_BUS
};
141 cbdata
.u
.watch_bus_data
.bus
= bus
;
142 cbdata
.u
.watch_bus_data
.msg
= msg
;
143 cbdata
.u
.watch_bus_data
.user
= user
;
147 return cbdata
.u
.watch_bus_data
.ret
;
150 void existing_new_pad_wrapper(GstElement
*bin
, GstPad
*pad
, gpointer user
)
152 struct cb_data cbdata
= { EXISTING_NEW_PAD
};
154 cbdata
.u
.pad_added_data
.element
= bin
;
155 cbdata
.u
.pad_added_data
.pad
= pad
;
156 cbdata
.u
.pad_added_data
.user
= user
;
161 gboolean
query_function_wrapper(GstPad
*pad
, GstObject
*parent
, GstQuery
*query
)
163 struct cb_data cbdata
= { QUERY_FUNCTION
};
165 cbdata
.u
.query_function_data
.pad
= pad
;
166 cbdata
.u
.query_function_data
.parent
= parent
;
167 cbdata
.u
.query_function_data
.query
= query
;
171 return cbdata
.u
.query_function_data
.ret
;
174 gboolean
activate_mode_wrapper(GstPad
*pad
, GstObject
*parent
, GstPadMode mode
, gboolean activate
)
176 struct cb_data cbdata
= { ACTIVATE_MODE
};
178 cbdata
.u
.activate_mode_data
.pad
= pad
;
179 cbdata
.u
.activate_mode_data
.parent
= parent
;
180 cbdata
.u
.activate_mode_data
.mode
= mode
;
181 cbdata
.u
.activate_mode_data
.activate
= activate
;
185 return cbdata
.u
.activate_mode_data
.ret
;
188 void no_more_pads_wrapper(GstElement
*element
, gpointer user
)
190 struct cb_data cbdata
= { NO_MORE_PADS
};
192 cbdata
.u
.no_more_pads_data
.element
= element
;
193 cbdata
.u
.no_more_pads_data
.user
= user
;
198 GstFlowReturn
request_buffer_src_wrapper(GstPad
*pad
, GstObject
*parent
, guint64 ofs
, guint len
,
201 struct cb_data cbdata
= { REQUEST_BUFFER_SRC
};
203 cbdata
.u
.getrange_data
.pad
= pad
;
204 cbdata
.u
.getrange_data
.parent
= parent
;
205 cbdata
.u
.getrange_data
.ofs
= ofs
;
206 cbdata
.u
.getrange_data
.len
= len
;
207 cbdata
.u
.getrange_data
.buf
= buf
;
211 return cbdata
.u
.getrange_data
.ret
;
214 gboolean
event_src_wrapper(GstPad
*pad
, GstObject
*parent
, GstEvent
*event
)
216 struct cb_data cbdata
= { EVENT_SRC
};
218 cbdata
.u
.event_src_data
.pad
= pad
;
219 cbdata
.u
.event_src_data
.parent
= parent
;
220 cbdata
.u
.event_src_data
.event
= event
;
224 return cbdata
.u
.event_src_data
.ret
;
227 gboolean
event_sink_wrapper(GstPad
*pad
, GstObject
*parent
, GstEvent
*event
)
229 struct cb_data cbdata
= { EVENT_SINK
};
231 cbdata
.u
.event_sink_data
.pad
= pad
;
232 cbdata
.u
.event_sink_data
.parent
= parent
;
233 cbdata
.u
.event_sink_data
.event
= event
;
237 return cbdata
.u
.event_sink_data
.ret
;
240 GstFlowReturn
got_data_sink_wrapper(GstPad
*pad
, GstObject
*parent
, GstBuffer
*buf
)
242 struct cb_data cbdata
= { GOT_DATA_SINK
};
244 cbdata
.u
.got_data_sink_data
.pad
= pad
;
245 cbdata
.u
.got_data_sink_data
.parent
= parent
;
246 cbdata
.u
.got_data_sink_data
.buf
= buf
;
250 return cbdata
.u
.got_data_sink_data
.ret
;
253 void removed_decoded_pad_wrapper(GstElement
*bin
, GstPad
*pad
, gpointer user
)
255 struct cb_data cbdata
= { REMOVED_DECODED_PAD
};
257 cbdata
.u
.pad_removed_data
.element
= bin
;
258 cbdata
.u
.pad_removed_data
.pad
= pad
;
259 cbdata
.u
.pad_removed_data
.user
= user
;
264 GstAutoplugSelectResult
autoplug_blacklist_wrapper(GstElement
*bin
, GstPad
*pad
,
265 GstCaps
*caps
, GstElementFactory
*fact
, gpointer user
)
267 struct cb_data cbdata
= { AUTOPLUG_BLACKLIST
};
269 cbdata
.u
.autoplug_blacklist_data
.bin
= bin
;
270 cbdata
.u
.autoplug_blacklist_data
.pad
= pad
;
271 cbdata
.u
.autoplug_blacklist_data
.caps
= caps
;
272 cbdata
.u
.autoplug_blacklist_data
.fact
= fact
;
273 cbdata
.u
.autoplug_blacklist_data
.user
= user
;
277 return cbdata
.u
.autoplug_blacklist_data
.ret
;
280 void unknown_type_wrapper(GstElement
*bin
, GstPad
*pad
, GstCaps
*caps
, gpointer user
)
282 struct cb_data cbdata
= { UNKNOWN_TYPE
};
284 cbdata
.u
.unknown_type_data
.bin
= bin
;
285 cbdata
.u
.unknown_type_data
.pad
= pad
;
286 cbdata
.u
.unknown_type_data
.caps
= caps
;
287 cbdata
.u
.unknown_type_data
.user
= user
;
292 gboolean
query_sink_wrapper(GstPad
*pad
, GstObject
*parent
, GstQuery
*query
)
294 struct cb_data cbdata
= { QUERY_SINK
};
296 cbdata
.u
.query_sink_data
.pad
= pad
;
297 cbdata
.u
.query_sink_data
.parent
= parent
;
298 cbdata
.u
.query_sink_data
.query
= query
;
302 return cbdata
.u
.query_sink_data
.ret
;