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