apple_glx_drawable.c revision ad503c41557606d15b0420c824369456f6d20a8f
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
40static pthread_mutex_t drawables_lock = PTHREAD_MUTEX_INITIALIZER;
41static struct apple_glx_drawable *drawables_list = NULL;
42
43static void
44lock_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
57static void
58unlock_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
71struct apple_glx_drawable *
72apple_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
90static void
91drawable_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
103static void
104drawable_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
117static void
118reference_drawable(struct apple_glx_drawable *d)
119{
120   d->lock(d);
121   d->reference_count++;
122   d->unlock(d);
123}
124
125static void
126release_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. */
135static bool
136destroy_drawable(struct apple_glx_drawable *d)
137{
138
139   d->lock(d);
140
141   if (d->reference_count > 0) {
142      d->unlock(d);
143      return false;
144   }
145
146   d->unlock(d);
147
148   if (d->previous) {
149      d->previous->next = d->next;
150   }
151   else {
152      /*
153       * The item must be at the head of the list, if it
154       * has no previous pointer.
155       */
156      drawables_list = d->next;
157   }
158
159   if (d->next)
160      d->next->previous = d->previous;
161
162   unlock_drawables_list();
163
164   if (d->callbacks.destroy) {
165      /*
166       * Warning: this causes other routines to be called (potentially)
167       * from surface_notify_handler.  It's probably best to not have
168       * any locks at this point locked.
169       */
170      d->callbacks.destroy(d->display, d);
171   }
172
173   apple_glx_diagnostic("%s: freeing %p\n", __func__, (void *) d);
174
175   free(d);
176
177   /* So that the locks are balanced and the caller correctly unlocks. */
178   lock_drawables_list();
179
180   return true;
181}
182
183/*
184 * This is typically called when a context is destroyed or the current
185 * drawable is made None.
186 */
187static bool
188destroy_drawable_callback(struct apple_glx_drawable *d)
189{
190   bool result;
191
192   d->lock(d);
193
194   apple_glx_diagnostic("%s: %p ->reference_count before -- %d\n", __func__,
195                        (void *) d, d->reference_count);
196
197   d->reference_count--;
198
199   if (d->reference_count > 0) {
200      d->unlock(d);
201      return false;
202   }
203
204   d->unlock(d);
205
206   lock_drawables_list();
207
208   result = destroy_drawable(d);
209
210   unlock_drawables_list();
211
212   return result;
213}
214
215static bool
216is_pbuffer(struct apple_glx_drawable *d)
217{
218   return APPLE_GLX_DRAWABLE_PBUFFER == d->type;
219}
220
221static bool
222is_pixmap(struct apple_glx_drawable *d)
223{
224   return APPLE_GLX_DRAWABLE_PIXMAP == d->type;
225}
226
227static void
228common_init(Display * dpy, GLXDrawable drawable, struct apple_glx_drawable *d)
229{
230   int err;
231   pthread_mutexattr_t attr;
232
233   d->display = dpy;
234   d->reference_count = 0;
235   d->drawable = drawable;
236   d->type = -1;
237
238   err = pthread_mutexattr_init(&attr);
239
240   if (err) {
241      fprintf(stderr, "pthread_mutexattr_init error: %d\n", err);
242      abort();
243   }
244
245   /*
246    * There are some patterns that require a recursive mutex,
247    * when working with locks that protect the apple_glx_drawable,
248    * and reference functions like ->reference, and ->release.
249    */
250   err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
251
252   if (err) {
253      fprintf(stderr, "error: setting pthread mutex type: %d\n", err);
254      abort();
255   }
256
257   err = pthread_mutex_init(&d->mutex, &attr);
258
259   if (err) {
260      fprintf(stderr, "pthread_mutex_init error: %d\n", err);
261      abort();
262   }
263
264   (void) pthread_mutexattr_destroy(&attr);
265
266   d->lock = drawable_lock;
267   d->unlock = drawable_unlock;
268
269   d->reference = reference_drawable;
270   d->release = release_drawable;
271
272   d->destroy = destroy_drawable_callback;
273
274   d->is_pbuffer = is_pbuffer;
275   d->is_pixmap = is_pixmap;
276
277   d->width = -1;
278   d->height = -1;
279   d->row_bytes = 0;
280   d->path[0] = '\0';
281   d->fd = -1;
282   d->buffer = NULL;
283   d->buffer_length = 0;
284
285   d->previous = NULL;
286   d->next = NULL;
287}
288
289static void
290link_tail(struct apple_glx_drawable *agd)
291{
292   lock_drawables_list();
293
294   /* Link the new drawable into the global list. */
295   agd->next = drawables_list;
296
297   if (drawables_list)
298      drawables_list->previous = agd;
299
300   drawables_list = agd;
301
302   unlock_drawables_list();
303}
304
305/*WARNING: this returns a locked and referenced object. */
306bool
307apple_glx_drawable_create(Display * dpy,
308                          int screen,
309                          GLXDrawable drawable,
310                          struct apple_glx_drawable **agdResult,
311                          struct apple_glx_drawable_callbacks *callbacks)
312{
313   struct apple_glx_drawable *d;
314
315   d = calloc(1, sizeof *d);
316
317   if (NULL == d) {
318      perror("malloc");
319      return true;
320   }
321
322   common_init(dpy, drawable, d);
323   d->type = callbacks->type;
324   d->callbacks = *callbacks;
325
326   d->reference(d);
327   d->lock(d);
328
329   link_tail(d);
330
331   apple_glx_diagnostic("%s: new drawable %p\n", __func__, (void *) d);
332
333   *agdResult = d;
334
335   return false;
336}
337
338static int error_count = 0;
339
340static int
341error_handler(Display * dpy, XErrorEvent * err)
342{
343   if (err->error_code == BadWindow) {
344      ++error_count;
345   }
346
347   return 0;
348}
349
350void
351apple_glx_garbage_collect_drawables(Display * dpy)
352{
353   struct apple_glx_drawable *d, *dnext;
354   Window root;
355   int x, y;
356   unsigned int width, height, bd, depth;
357   int (*old_handler) (Display *, XErrorEvent *);
358
359
360   if (NULL == drawables_list)
361      return;
362
363   old_handler = XSetErrorHandler(error_handler);
364
365   XSync(dpy, False);
366
367   lock_drawables_list();
368
369   for (d = drawables_list; d;) {
370      dnext = d->next;
371
372      d->lock(d);
373
374      if (d->reference_count > 0) {
375         /*
376          * Skip this, because some context still retains a reference
377          * to the drawable.
378          */
379         d->unlock(d);
380         d = dnext;
381         continue;
382      }
383
384      d->unlock(d);
385
386      error_count = 0;
387
388      /*
389       * Mesa uses XGetWindowAttributes, but some of these things are
390       * most definitely not Windows, and that's against the rules.
391       * XGetGeometry on the other hand is legal with a Pixmap and Window.
392       */
393      XGetGeometry(dpy, d->drawable, &root, &x, &y, &width, &height, &bd,
394                   &depth);
395
396      if (error_count > 0) {
397         /*
398          * Note: this may not actually destroy the drawable.
399          * If another context retains a reference to the drawable
400          * after the reference count test above.
401          */
402         (void) destroy_drawable(d);
403         error_count = 0;
404      }
405
406      d = dnext;
407   }
408
409   XSetErrorHandler(old_handler);
410
411   unlock_drawables_list();
412}
413
414unsigned int
415apple_glx_get_drawable_count(void)
416{
417   unsigned int result = 0;
418   struct apple_glx_drawable *d;
419
420   lock_drawables_list();
421
422   for (d = drawables_list; d; d = d->next)
423      ++result;
424
425   unlock_drawables_list();
426
427   return result;
428}
429
430struct apple_glx_drawable *
431apple_glx_drawable_find_by_type(GLXDrawable drawable, int type, int flags)
432{
433   struct apple_glx_drawable *d;
434
435   lock_drawables_list();
436
437   for (d = drawables_list; d; d = d->next) {
438      if (d->type == type && d->drawable == drawable) {
439         if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
440            d->reference(d);
441
442         if (flags & APPLE_GLX_DRAWABLE_LOCK)
443            d->lock(d);
444
445         unlock_drawables_list();
446
447         return d;
448      }
449   }
450
451   unlock_drawables_list();
452
453   return NULL;
454}
455
456struct apple_glx_drawable *
457apple_glx_drawable_find(GLXDrawable drawable, int flags)
458{
459   struct apple_glx_drawable *d;
460
461   lock_drawables_list();
462
463   for (d = drawables_list; d; d = d->next) {
464      if (d->drawable == drawable) {
465         if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
466            d->reference(d);
467
468         if (flags & APPLE_GLX_DRAWABLE_LOCK)
469            d->lock(d);
470
471         unlock_drawables_list();
472
473         return d;
474      }
475   }
476
477   unlock_drawables_list();
478
479   return NULL;
480}
481
482/* Return true if the type is valid for the drawable. */
483bool
484apple_glx_drawable_destroy_by_type(Display * dpy,
485                                   GLXDrawable drawable, int type)
486{
487   struct apple_glx_drawable *d;
488
489   lock_drawables_list();
490
491   for (d = drawables_list; d; d = d->next) {
492      if (drawable == d->drawable && type == d->type) {
493         /*
494          * The user has requested that we destroy this resource.
495          * However, there may be references in the contexts to it, so
496          * release it, and call destroy_drawable which doesn't destroy
497          * if the reference_count is > 0.
498          */
499         d->release(d);
500
501         apple_glx_diagnostic("%s d->reference_count %d\n",
502                              __func__, d->reference_count);
503
504         destroy_drawable(d);
505         unlock_drawables_list();
506         return true;
507      }
508   }
509
510   unlock_drawables_list();
511
512   return false;
513}
514
515struct apple_glx_drawable *
516apple_glx_drawable_find_by_uid(unsigned int uid, int flags)
517{
518   struct apple_glx_drawable *d;
519
520   lock_drawables_list();
521
522   for (d = drawables_list; d; d = d->next) {
523      /* Only surfaces have a uid. */
524      if (APPLE_GLX_DRAWABLE_SURFACE == d->type) {
525         if (d->types.surface.uid == uid) {
526            if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
527               d->reference(d);
528
529            if (flags & APPLE_GLX_DRAWABLE_LOCK)
530               d->lock(d);
531
532            unlock_drawables_list();
533
534            return d;
535         }
536      }
537   }
538
539   unlock_drawables_list();
540
541   return NULL;
542}
543