Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /* Cairo - a vector graphics library with display and print output
  2.  *
  3.  * Copyright © 2007 Chris Wilson
  4.  * Copyright © 2009 Intel Corporation
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it either under the terms of the GNU Lesser General Public
  8.  * License version 2.1 as published by the Free Software Foundation
  9.  * (the "LGPL") or, at your option, under the terms of the Mozilla
  10.  * Public License Version 1.1 (the "MPL"). If you do not alter this
  11.  * notice, a recipient may use your version of this file under either
  12.  * the MPL or the LGPL.
  13.  *
  14.  * You should have received a copy of the LGPL along with this library
  15.  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
  16.  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
  17.  * You should have received a copy of the MPL along with this library
  18.  * in the file COPYING-MPL-1.1
  19.  *
  20.  * The contents of this file are subject to the Mozilla Public License
  21.  * Version 1.1 (the "License"); you may not use this file except in
  22.  * compliance with the License. You may obtain a copy of the License at
  23.  * http://www.mozilla.org/MPL/
  24.  *
  25.  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
  26.  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
  27.  * the specific language governing rights and limitations.
  28.  *
  29.  * The Original Code is the cairo graphics library.
  30.  *
  31.  * The Initial Developer of the Original Code is Red Hat, Inc.
  32.  *
  33.  * Contributors(s):
  34.  *      Chris Wilson <chris@chris-wilson.co.uk>
  35.  */
  36.  
  37. #include "cairoint.h"
  38.  
  39. #if CAIRO_HAS_XCB_SHM_FUNCTIONS
  40.  
  41. #include "cairo-xcb-private.h"
  42. #include "cairo-list-inline.h"
  43. #include "cairo-mempool-private.h"
  44.  
  45. #include <xcb/shm.h>
  46. #include <sys/ipc.h>
  47. #include <sys/shm.h>
  48. #include <errno.h>
  49.  
  50. #define CAIRO_MAX_SHM_MEMORY (16*1024*1024)
  51.  
  52. /* a simple buddy allocator for memory pools
  53.  * XXX fragmentation? use Doug Lea's malloc?
  54.  */
  55.  
  56. typedef struct _cairo_xcb_shm_mem_block cairo_xcb_shm_mem_block_t;
  57.  
  58. typedef enum {
  59.     PENDING_WAIT,
  60.     PENDING_POLL
  61. } shm_wait_type_t;
  62.  
  63. struct _cairo_xcb_shm_mem_pool {
  64.     int shmid;
  65.     uint32_t shmseg;
  66.     void *shm;
  67.  
  68.     cairo_mempool_t mem;
  69.  
  70.     cairo_list_t link;
  71. };
  72.  
  73. static void
  74. _cairo_xcb_shm_mem_pool_destroy (cairo_xcb_shm_mem_pool_t *pool)
  75. {
  76.     cairo_list_del (&pool->link);
  77.  
  78.     shmdt (pool->shm);
  79.     _cairo_mempool_fini (&pool->mem);
  80.  
  81.     free (pool);
  82. }
  83.  
  84. static void
  85. _cairo_xcb_shm_info_finalize (cairo_xcb_shm_info_t *shm_info)
  86. {
  87.     cairo_xcb_connection_t *connection = shm_info->connection;
  88.  
  89.     assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
  90.  
  91.     _cairo_mempool_free (&shm_info->pool->mem, shm_info->mem);
  92.     _cairo_freepool_free (&connection->shm_info_freelist, shm_info);
  93.  
  94.     /* scan for old, unused pools - hold at least one in reserve */
  95.     if (! cairo_list_is_singular (&connection->shm_pools))
  96.     {
  97.         cairo_xcb_shm_mem_pool_t *pool, *next;
  98.         cairo_list_t head;
  99.  
  100.         cairo_list_init (&head);
  101.         cairo_list_move (connection->shm_pools.next, &head);
  102.  
  103.         cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
  104.                                        &connection->shm_pools, link)
  105.         {
  106.             if (pool->mem.free_bytes == pool->mem.max_bytes) {
  107.                 _cairo_xcb_connection_shm_detach (connection, pool->shmseg);
  108.                 _cairo_xcb_shm_mem_pool_destroy (pool);
  109.             }
  110.         }
  111.  
  112.         cairo_list_move (head.next, &connection->shm_pools);
  113.     }
  114. }
  115.  
  116. static void
  117. _cairo_xcb_shm_process_pending (cairo_xcb_connection_t *connection, shm_wait_type_t wait)
  118. {
  119.     cairo_xcb_shm_info_t *info, *next;
  120.     xcb_get_input_focus_reply_t *reply;
  121.  
  122.     assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
  123.     cairo_list_foreach_entry_safe (info, next, cairo_xcb_shm_info_t,
  124.                                    &connection->shm_pending, pending)
  125.     {
  126.         switch (wait) {
  127.         case PENDING_WAIT:
  128.              reply = xcb_wait_for_reply (connection->xcb_connection,
  129.                                          info->sync.sequence, NULL);
  130.              break;
  131.         case PENDING_POLL:
  132.             if (! xcb_poll_for_reply (connection->xcb_connection,
  133.                                       info->sync.sequence,
  134.                                       (void **) &reply, NULL))
  135.                 /* We cannot be sure the server finished with this image yet, so
  136.                  * try again later. All other shm info are guaranteed to have a
  137.                  * larger sequence number and thus don't have to be checked. */
  138.                 return;
  139.             break;
  140.         default:
  141.             /* silence Clang static analyzer warning */
  142.             ASSERT_NOT_REACHED;
  143.             reply = NULL;
  144.         }
  145.  
  146.         free (reply);
  147.         cairo_list_del (&info->pending);
  148.         _cairo_xcb_shm_info_finalize (info);
  149.     }
  150. }
  151.  
  152. cairo_int_status_t
  153. _cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *connection,
  154.                                          size_t size,
  155.                                          cairo_bool_t might_reuse,
  156.                                          cairo_xcb_shm_info_t **shm_info_out)
  157. {
  158.     cairo_xcb_shm_info_t *shm_info;
  159.     cairo_xcb_shm_mem_pool_t *pool, *next;
  160.     size_t bytes, maxbits = 16, minbits = 8;
  161.     size_t shm_allocated = 0;
  162.     void *mem = NULL;
  163.     cairo_status_t status;
  164.  
  165.     assert (connection->flags & CAIRO_XCB_HAS_SHM);
  166.  
  167.     CAIRO_MUTEX_LOCK (connection->shm_mutex);
  168.     _cairo_xcb_shm_process_pending (connection, PENDING_POLL);
  169.  
  170.     if (might_reuse) {
  171.         cairo_list_foreach_entry (shm_info, cairo_xcb_shm_info_t,
  172.                 &connection->shm_pending, pending) {
  173.             if (shm_info->size >= size) {
  174.                 cairo_list_del (&shm_info->pending);
  175.                 CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
  176.  
  177.                 xcb_discard_reply (connection->xcb_connection, shm_info->sync.sequence);
  178.                 shm_info->sync.sequence = XCB_NONE;
  179.  
  180.                 *shm_info_out = shm_info;
  181.                 return CAIRO_STATUS_SUCCESS;
  182.             }
  183.         }
  184.     }
  185.  
  186.     cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
  187.                                    &connection->shm_pools, link)
  188.     {
  189.         if (pool->mem.free_bytes > size) {
  190.             mem = _cairo_mempool_alloc (&pool->mem, size);
  191.             if (mem != NULL) {
  192.                 /* keep the active pools towards the front */
  193.                 cairo_list_move (&pool->link, &connection->shm_pools);
  194.                 goto allocate_shm_info;
  195.             }
  196.         }
  197.         /* scan for old, unused pools */
  198.         if (pool->mem.free_bytes == pool->mem.max_bytes) {
  199.             _cairo_xcb_connection_shm_detach (connection,
  200.                                               pool->shmseg);
  201.             _cairo_xcb_shm_mem_pool_destroy (pool);
  202.         } else {
  203.             shm_allocated += pool->mem.max_bytes;
  204.         }
  205.     }
  206.  
  207.     if (unlikely (shm_allocated >= CAIRO_MAX_SHM_MEMORY)) {
  208.         CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
  209.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  210.     }
  211.  
  212.     pool = malloc (sizeof (cairo_xcb_shm_mem_pool_t));
  213.     if (unlikely (pool == NULL)) {
  214.         CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
  215.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  216.     }
  217.  
  218.     bytes = 1 << maxbits;
  219.     while (bytes <= size)
  220.         bytes <<= 1, maxbits++;
  221.     bytes <<= 3;
  222.  
  223.     do {
  224.         pool->shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
  225.         if (pool->shmid != -1)
  226.             break;
  227.  
  228.         /* If the allocation failed because we asked for too much memory, we try
  229.          * again with a smaller request, as long as our allocation still fits. */
  230.         bytes >>= 1;
  231.         if (errno != EINVAL || bytes < size)
  232.             break;
  233.     } while (TRUE);
  234.     if (pool->shmid == -1) {
  235.         int err = errno;
  236.         if (! (err == EINVAL || err == ENOMEM))
  237.             connection->flags &= ~CAIRO_XCB_HAS_SHM;
  238.         free (pool);
  239.         CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
  240.         return CAIRO_INT_STATUS_UNSUPPORTED;
  241.     }
  242.  
  243.     pool->shm = shmat (pool->shmid, NULL, 0);
  244.     if (unlikely (pool->shm == (char *) -1)) {
  245.         shmctl (pool->shmid, IPC_RMID, NULL);
  246.         free (pool);
  247.         CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
  248.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  249.     }
  250.  
  251.     status = _cairo_mempool_init (&pool->mem, pool->shm, bytes,
  252.                                   minbits, maxbits - minbits + 1);
  253.     if (unlikely (status)) {
  254.         shmdt (pool->shm);
  255.         free (pool);
  256.         CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
  257.         return status;
  258.     }
  259.  
  260.     pool->shmseg = _cairo_xcb_connection_shm_attach (connection, pool->shmid, FALSE);
  261.     shmctl (pool->shmid, IPC_RMID, NULL);
  262.  
  263.     cairo_list_add (&pool->link, &connection->shm_pools);
  264.     mem = _cairo_mempool_alloc (&pool->mem, size);
  265.  
  266.   allocate_shm_info:
  267.     shm_info = _cairo_freepool_alloc (&connection->shm_info_freelist);
  268.     if (unlikely (shm_info == NULL)) {
  269.         _cairo_mempool_free (&pool->mem, mem);
  270.         CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
  271.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  272.     }
  273.  
  274.     shm_info->connection = connection;
  275.     shm_info->pool = pool;
  276.     shm_info->shm = pool->shmseg;
  277.     shm_info->size = size;
  278.     shm_info->offset = (char *) mem - (char *) pool->shm;
  279.     shm_info->mem = mem;
  280.     shm_info->sync.sequence = XCB_NONE;
  281.  
  282.     /* scan for old, unused pools */
  283.     cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
  284.                                    &connection->shm_pools, link)
  285.     {
  286.         if (pool->mem.free_bytes == pool->mem.max_bytes) {
  287.             _cairo_xcb_connection_shm_detach (connection,
  288.                                               pool->shmseg);
  289.             _cairo_xcb_shm_mem_pool_destroy (pool);
  290.         }
  291.     }
  292.     CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
  293.  
  294.     *shm_info_out = shm_info;
  295.     return CAIRO_STATUS_SUCCESS;
  296. }
  297.  
  298. void
  299. _cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info)
  300. {
  301.     cairo_xcb_connection_t *connection = shm_info->connection;
  302.  
  303.     /* We can only return shm_info->mem to the allocator when we can be sure
  304.      * that the X server no longer reads from it. Since the X server processes
  305.      * requests in order, we send a GetInputFocus here.
  306.      * _cairo_xcb_shm_process_pending () will later check if the reply for that
  307.      * request was received and then actually mark this memory area as free. */
  308.  
  309.     CAIRO_MUTEX_LOCK (connection->shm_mutex);
  310.     assert (shm_info->sync.sequence == XCB_NONE);
  311.     shm_info->sync = xcb_get_input_focus (connection->xcb_connection);
  312.  
  313.     cairo_list_init (&shm_info->pending);
  314.     cairo_list_add_tail (&shm_info->pending, &connection->shm_pending);
  315.     CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
  316. }
  317.  
  318. void
  319. _cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection)
  320. {
  321.     CAIRO_MUTEX_LOCK (connection->shm_mutex);
  322.     _cairo_xcb_shm_process_pending (connection, PENDING_WAIT);
  323.     CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
  324. }
  325.  
  326. void
  327. _cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection)
  328. {
  329.     assert (cairo_list_is_empty (&connection->shm_pending));
  330.     while (! cairo_list_is_empty (&connection->shm_pools)) {
  331.         _cairo_xcb_shm_mem_pool_destroy (cairo_list_first_entry (&connection->shm_pools,
  332.                                                                  cairo_xcb_shm_mem_pool_t,
  333.                                                                  link));
  334.     }
  335. }
  336.  
  337. #endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */
  338.