darwin: Eliminate a pthread mutex leak
[mesa.git] / src / glx / apple / apple_glx_drawable.c
1 /*
2 Copyright (c) 2008, 2009 Apple Inc.
3
4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation files
6 (the "Software"), to deal in the Software without restriction,
7 including without limitation the rights to use, copy, modify, merge,
8 publish, distribute, sublicense, and/or sell copies of the Software,
9 and to permit persons to whom the Software is furnished to do so,
10 subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
19 HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 DEALINGS IN THE SOFTWARE.
23
24 Except as contained in this notice, the name(s) of the above
25 copyright holders shall not be used in advertising or otherwise to
26 promote the sale, use or other dealings in this Software without
27 prior written authorization.
28 */
29
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <assert.h>
34 #include <pthread.h>
35 #include "apple_glx.h"
36 #include "apple_glx_context.h"
37 #include "apple_glx_drawable.h"
38 #include "appledri.h"
39
40 static pthread_mutex_t drawables_lock = PTHREAD_MUTEX_INITIALIZER;
41 static struct apple_glx_drawable *drawables_list = NULL;
42
43 static void
44 lock_drawables_list(void)
45 {
46 int err;
47
48 err = pthread_mutex_lock(&drawables_lock);
49
50 if (err) {
51 fprintf(stderr, "pthread_mutex_lock failure in %s: %d\n",
52 __func__, err);
53 abort();
54 }
55 }
56
57 static void
58 unlock_drawables_list(void)
59 {
60 int err;
61
62 err = pthread_mutex_unlock(&drawables_lock);
63
64 if (err) {
65 fprintf(stderr, "pthread_mutex_unlock failure in %s: %d\n",
66 __func__, err);
67 abort();
68 }
69 }
70
71 struct apple_glx_drawable *
72 apple_glx_find_drawable(Display * dpy, GLXDrawable drawable)
73 {
74 struct apple_glx_drawable *i, *agd = NULL;
75
76 lock_drawables_list();
77
78 for (i = drawables_list; i; i = i->next) {
79 if (i->drawable == drawable) {
80 agd = i;
81 break;
82 }
83 }
84
85 unlock_drawables_list();
86
87 return agd;
88 }
89
90 static void
91 drawable_lock(struct apple_glx_drawable *agd)
92 {
93 int err;
94
95 err = pthread_mutex_lock(&agd->mutex);
96
97 if (err) {
98 fprintf(stderr, "pthread_mutex_lock error: %d\n", err);
99 abort();
100 }
101 }
102
103 static void
104 drawable_unlock(struct apple_glx_drawable *d)
105 {
106 int err;
107
108 err = pthread_mutex_unlock(&d->mutex);
109
110 if (err) {
111 fprintf(stderr, "pthread_mutex_unlock error: %d\n", err);
112 abort();
113 }
114 }
115
116
117 static void
118 reference_drawable(struct apple_glx_drawable *d)
119 {
120 d->lock(d);
121 d->reference_count++;
122 d->unlock(d);
123 }
124
125 static void
126 release_drawable(struct apple_glx_drawable *d)
127 {
128 d->lock(d);
129 d->reference_count--;
130 d->unlock(d);
131 }
132
133 /* The drawables list must be locked prior to calling this. */
134 /* Return true if the drawable was destroyed. */
135 static bool
136 destroy_drawable(struct apple_glx_drawable *d)
137 {
138 int err;
139
140 d->lock(d);
141
142 if (d->reference_count > 0) {
143 d->unlock(d);
144 return false;
145 }
146
147 d->unlock(d);
148
149 if (d->previous) {
150 d->previous->next = d->next;
151 }
152 else {
153 /*
154 * The item must be at the head of the list, if it
155 * has no previous pointer.
156 */
157 drawables_list = d->next;
158 }
159
160 if (d->next)
161 d->next->previous = d->previous;
162
163 unlock_drawables_list();
164
165 if (d->callbacks.destroy) {
166 /*
167 * Warning: this causes other routines to be called (potentially)
168 * from surface_notify_handler. It's probably best to not have
169 * any locks at this point locked.
170 */
171 d->callbacks.destroy(d->display, d);
172 }
173
174 apple_glx_diagnostic("%s: freeing %p\n", __func__, (void *) d);
175
176 err = pthread_mutex_destroy(&d->mutex);
177 if (err) {
178 fprintf(stderr, "pthread_mutex_destroy error: %s\n", strerror(err));
179 abort();
180 }
181
182 free(d);
183
184 /* So that the locks are balanced and the caller correctly unlocks. */
185 lock_drawables_list();
186
187 return true;
188 }
189
190 /*
191 * This is typically called when a context is destroyed or the current
192 * drawable is made None.
193 */
194 static bool
195 destroy_drawable_callback(struct apple_glx_drawable *d)
196 {
197 bool result;
198
199 d->lock(d);
200
201 apple_glx_diagnostic("%s: %p ->reference_count before -- %d\n", __func__,
202 (void *) d, d->reference_count);
203
204 d->reference_count--;
205
206 if (d->reference_count > 0) {
207 d->unlock(d);
208 return false;
209 }
210
211 d->unlock(d);
212
213 lock_drawables_list();
214
215 result = destroy_drawable(d);
216
217 unlock_drawables_list();
218
219 return result;
220 }
221
222 static bool
223 is_pbuffer(struct apple_glx_drawable *d)
224 {
225 return APPLE_GLX_DRAWABLE_PBUFFER == d->type;
226 }
227
228 static bool
229 is_pixmap(struct apple_glx_drawable *d)
230 {
231 return APPLE_GLX_DRAWABLE_PIXMAP == d->type;
232 }
233
234 static void
235 common_init(Display * dpy, GLXDrawable drawable, struct apple_glx_drawable *d)
236 {
237 int err;
238 pthread_mutexattr_t attr;
239
240 d->display = dpy;
241 d->reference_count = 0;
242 d->drawable = drawable;
243 d->type = -1;
244
245 err = pthread_mutexattr_init(&attr);
246
247 if (err) {
248 fprintf(stderr, "pthread_mutexattr_init error: %d\n", err);
249 abort();
250 }
251
252 /*
253 * There are some patterns that require a recursive mutex,
254 * when working with locks that protect the apple_glx_drawable,
255 * and reference functions like ->reference, and ->release.
256 */
257 err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
258
259 if (err) {
260 fprintf(stderr, "error: setting pthread mutex type: %d\n", err);
261 abort();
262 }
263
264 err = pthread_mutex_init(&d->mutex, &attr);
265
266 if (err) {
267 fprintf(stderr, "pthread_mutex_init error: %d\n", err);
268 abort();
269 }
270
271 (void) pthread_mutexattr_destroy(&attr);
272
273 d->lock = drawable_lock;
274 d->unlock = drawable_unlock;
275
276 d->reference = reference_drawable;
277 d->release = release_drawable;
278
279 d->destroy = destroy_drawable_callback;
280
281 d->is_pbuffer = is_pbuffer;
282 d->is_pixmap = is_pixmap;
283
284 d->width = -1;
285 d->height = -1;
286 d->row_bytes = 0;
287 d->path[0] = '\0';
288 d->fd = -1;
289 d->buffer = NULL;
290 d->buffer_length = 0;
291
292 d->previous = NULL;
293 d->next = NULL;
294 }
295
296 static void
297 link_tail(struct apple_glx_drawable *agd)
298 {
299 lock_drawables_list();
300
301 /* Link the new drawable into the global list. */
302 agd->next = drawables_list;
303
304 if (drawables_list)
305 drawables_list->previous = agd;
306
307 drawables_list = agd;
308
309 unlock_drawables_list();
310 }
311
312 /*WARNING: this returns a locked and referenced object. */
313 bool
314 apple_glx_drawable_create(Display * dpy,
315 int screen,
316 GLXDrawable drawable,
317 struct apple_glx_drawable **agdResult,
318 struct apple_glx_drawable_callbacks *callbacks)
319 {
320 struct apple_glx_drawable *d;
321
322 d = calloc(1, sizeof *d);
323
324 if (NULL == d) {
325 perror("malloc");
326 return true;
327 }
328
329 common_init(dpy, drawable, d);
330 d->type = callbacks->type;
331 d->callbacks = *callbacks;
332
333 d->reference(d);
334 d->lock(d);
335
336 link_tail(d);
337
338 apple_glx_diagnostic("%s: new drawable %p\n", __func__, (void *) d);
339
340 *agdResult = d;
341
342 return false;
343 }
344
345 static int error_count = 0;
346
347 static int
348 error_handler(Display * dpy, XErrorEvent * err)
349 {
350 if (err->error_code == BadWindow) {
351 ++error_count;
352 }
353
354 return 0;
355 }
356
357 void
358 apple_glx_garbage_collect_drawables(Display * dpy)
359 {
360 struct apple_glx_drawable *d, *dnext;
361 Window root;
362 int x, y;
363 unsigned int width, height, bd, depth;
364 int (*old_handler) (Display *, XErrorEvent *);
365
366
367 if (NULL == drawables_list)
368 return;
369
370 old_handler = XSetErrorHandler(error_handler);
371
372 XSync(dpy, False);
373
374 lock_drawables_list();
375
376 for (d = drawables_list; d;) {
377 dnext = d->next;
378
379 d->lock(d);
380
381 if (d->reference_count > 0) {
382 /*
383 * Skip this, because some context still retains a reference
384 * to the drawable.
385 */
386 d->unlock(d);
387 d = dnext;
388 continue;
389 }
390
391 d->unlock(d);
392
393 error_count = 0;
394
395 /*
396 * Mesa uses XGetWindowAttributes, but some of these things are
397 * most definitely not Windows, and that's against the rules.
398 * XGetGeometry on the other hand is legal with a Pixmap and Window.
399 */
400 XGetGeometry(dpy, d->drawable, &root, &x, &y, &width, &height, &bd,
401 &depth);
402
403 if (error_count > 0) {
404 /*
405 * Note: this may not actually destroy the drawable.
406 * If another context retains a reference to the drawable
407 * after the reference count test above.
408 */
409 (void) destroy_drawable(d);
410 error_count = 0;
411 }
412
413 d = dnext;
414 }
415
416 XSetErrorHandler(old_handler);
417
418 unlock_drawables_list();
419 }
420
421 unsigned int
422 apple_glx_get_drawable_count(void)
423 {
424 unsigned int result = 0;
425 struct apple_glx_drawable *d;
426
427 lock_drawables_list();
428
429 for (d = drawables_list; d; d = d->next)
430 ++result;
431
432 unlock_drawables_list();
433
434 return result;
435 }
436
437 struct apple_glx_drawable *
438 apple_glx_drawable_find_by_type(GLXDrawable drawable, int type, int flags)
439 {
440 struct apple_glx_drawable *d;
441
442 lock_drawables_list();
443
444 for (d = drawables_list; d; d = d->next) {
445 if (d->type == type && d->drawable == drawable) {
446 if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
447 d->reference(d);
448
449 if (flags & APPLE_GLX_DRAWABLE_LOCK)
450 d->lock(d);
451
452 unlock_drawables_list();
453
454 return d;
455 }
456 }
457
458 unlock_drawables_list();
459
460 return NULL;
461 }
462
463 struct apple_glx_drawable *
464 apple_glx_drawable_find(GLXDrawable drawable, int flags)
465 {
466 struct apple_glx_drawable *d;
467
468 lock_drawables_list();
469
470 for (d = drawables_list; d; d = d->next) {
471 if (d->drawable == drawable) {
472 if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
473 d->reference(d);
474
475 if (flags & APPLE_GLX_DRAWABLE_LOCK)
476 d->lock(d);
477
478 unlock_drawables_list();
479
480 return d;
481 }
482 }
483
484 unlock_drawables_list();
485
486 return NULL;
487 }
488
489 /* Return true if the type is valid for the drawable. */
490 bool
491 apple_glx_drawable_destroy_by_type(Display * dpy,
492 GLXDrawable drawable, int type)
493 {
494 struct apple_glx_drawable *d;
495
496 lock_drawables_list();
497
498 for (d = drawables_list; d; d = d->next) {
499 if (drawable == d->drawable && type == d->type) {
500 /*
501 * The user has requested that we destroy this resource.
502 * However, there may be references in the contexts to it, so
503 * release it, and call destroy_drawable which doesn't destroy
504 * if the reference_count is > 0.
505 */
506 d->release(d);
507
508 apple_glx_diagnostic("%s d->reference_count %d\n",
509 __func__, d->reference_count);
510
511 destroy_drawable(d);
512 unlock_drawables_list();
513 return true;
514 }
515 }
516
517 unlock_drawables_list();
518
519 return false;
520 }
521
522 struct apple_glx_drawable *
523 apple_glx_drawable_find_by_uid(unsigned int uid, int flags)
524 {
525 struct apple_glx_drawable *d;
526
527 lock_drawables_list();
528
529 for (d = drawables_list; d; d = d->next) {
530 /* Only surfaces have a uid. */
531 if (APPLE_GLX_DRAWABLE_SURFACE == d->type) {
532 if (d->types.surface.uid == uid) {
533 if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
534 d->reference(d);
535
536 if (flags & APPLE_GLX_DRAWABLE_LOCK)
537 d->lock(d);
538
539 unlock_drawables_list();
540
541 return d;
542 }
543 }
544 }
545
546 unlock_drawables_list();
547
548 return NULL;
549 }