diff options
-rw-r--r-- | Xext/Makefile.am | 2 | ||||
-rw-r--r-- | Xext/hashtable.c | 291 | ||||
-rw-r--r-- | Xext/hashtable.h | 137 | ||||
-rw-r--r-- | Xext/xres.c | 829 | ||||
-rw-r--r-- | composite/compext.c | 25 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | dix/resource.c | 383 | ||||
-rw-r--r-- | include/protocol-versions.h | 2 | ||||
-rw-r--r-- | include/resource.h | 58 | ||||
-rw-r--r-- | render/picture.c | 24 | ||||
-rw-r--r-- | test/Makefile.am | 3 | ||||
-rw-r--r-- | test/hashtabletest.c | 162 |
12 files changed, 1882 insertions, 36 deletions
diff --git a/Xext/Makefile.am b/Xext/Makefile.am index cb432e00e..5929a3e49 100644 --- a/Xext/Makefile.am +++ b/Xext/Makefile.am @@ -50,7 +50,7 @@ MODULE_SRCS += $(XV_SRCS) endif # XResource extension: lets clients get data about per-client resource usage -RES_SRCS = xres.c +RES_SRCS = hashtable.c xres.c if RES MODULE_SRCS += $(RES_SRCS) endif diff --git a/Xext/hashtable.c b/Xext/hashtable.c new file mode 100644 index 000000000..2adf92e56 --- /dev/null +++ b/Xext/hashtable.c @@ -0,0 +1,291 @@ +#include <stdlib.h> +#include "misc.h" +#include "hashtable.h" + +/* HashResourceID */ +#include "resource.h" + +#define INITHASHSIZE 6 +#define MAXHASHSIZE 11 + +struct HashTableRec { + int keySize; + int dataSize; + + int elements; /* number of elements inserted */ + int bucketBits; /* number of buckets is 1 << bucketBits */ + struct xorg_list *buckets; /* array of bucket list heads */ + + HashFunc hash; + HashCompareFunc compare; + + pointer cdata; +}; + +typedef struct { + struct xorg_list l; + void *key; + void *data; +} BucketRec, *BucketPtr; + +HashTable +ht_create(int keySize, + int dataSize, + HashFunc hash, + HashCompareFunc compare, + pointer cdata) +{ + int c; + int numBuckets; + HashTable ht = malloc(sizeof(struct HashTableRec)); + + if (!ht) { + return NULL; + } + + ht->keySize = keySize; + ht->dataSize = dataSize; + ht->hash = hash; + ht->compare = compare; + ht->elements = 0; + ht->bucketBits = INITHASHSIZE; + numBuckets = 1 << ht->bucketBits; + ht->buckets = malloc(numBuckets * sizeof(*ht->buckets)); + ht->cdata = cdata; + + if (ht->buckets) { + for (c = 0; c < numBuckets; ++c) { + xorg_list_init(&ht->buckets[c]); + } + return ht; + } else { + free(ht); + return NULL; + } +} + +void +ht_destroy(HashTable ht) +{ + int c; + BucketPtr it, tmp; + int numBuckets = 1 << ht->bucketBits; + for (c = 0; c < numBuckets; ++c) { + xorg_list_for_each_entry_safe(it, tmp, &ht->buckets[c], l) { + xorg_list_del(&it->l); + free(it); + } + } + free(ht->buckets); +} + +static Bool +double_size(HashTable ht) +{ + struct xorg_list *newBuckets; + int numBuckets = 1 << ht->bucketBits; + int newBucketBits = ht->bucketBits + 1; + int newNumBuckets = 1 << newBucketBits; + int c; + + newBuckets = malloc(newNumBuckets * sizeof(*ht->buckets)); + if (newBuckets) { + for (c = 0; c < newNumBuckets; ++c) { + xorg_list_init(&newBuckets[c]); + } + + for (c = 0; c < numBuckets; ++c) { + BucketPtr it, tmp; + xorg_list_for_each_entry_safe(it, tmp, &ht->buckets[c], l) { + struct xorg_list *newBucket = + &newBuckets[ht->hash(ht->cdata, it->key, newBucketBits)]; + xorg_list_del(&it->l); + xorg_list_add(&it->l, newBucket); + } + } + free(ht->buckets); + + ht->buckets = newBuckets; + ht->bucketBits = newBucketBits; + return TRUE; + } else { + return FALSE; + } +} + +pointer +ht_add(HashTable ht, pointer key) +{ + unsigned index = ht->hash(ht->cdata, key, ht->bucketBits); + struct xorg_list *bucket = &ht->buckets[index]; + BucketRec *elem = calloc(1, sizeof(BucketRec)); + if (!elem) { + goto outOfMemory; + } + elem->key = malloc(ht->keySize); + if (!elem->key) { + goto outOfMemory; + } + /* we avoid signaling an out-of-memory error if dataSize is 0 */ + elem->data = calloc(1, ht->dataSize); + if (ht->dataSize && !elem->data) { + goto outOfMemory; + } + xorg_list_add(&elem->l, bucket); + ++ht->elements; + + memcpy(elem->key, key, ht->keySize); + + if (ht->elements > 4 * (1 << ht->bucketBits) && + ht->bucketBits < MAXHASHSIZE) { + if (!double_size(ht)) { + --ht->elements; + xorg_list_del(&elem->l); + goto outOfMemory; + } + } + + /* if memory allocation has failed due to dataSize being 0, return + a "dummy" pointer pointing at the of the key */ + return elem->data ? elem->data : ((char*) elem->key + ht->keySize); + + outOfMemory: + if (elem) { + free(elem->key); + free(elem->data); + free(elem); + } + + return NULL; +} + +void +ht_remove(HashTable ht, pointer key) +{ + unsigned index = ht->hash(ht->cdata, key, ht->bucketBits); + struct xorg_list *bucket = &ht->buckets[index]; + BucketPtr it; + + xorg_list_for_each_entry(it, bucket, l) { + if (ht->compare(ht->cdata, key, it->key) == 0) { + xorg_list_del(&it->l); + --ht->elements; + free(it->key); + free(it->data); + free(it); + return; + } + } +} + +pointer +ht_find(HashTable ht, pointer key) +{ + unsigned index = ht->hash(ht->cdata, key, ht->bucketBits); + struct xorg_list *bucket = &ht->buckets[index]; + BucketPtr it; + + xorg_list_for_each_entry(it, bucket, l) { + if (ht->compare(ht->cdata, key, it->key) == 0) { + return it->data ? it->data : ((char*) it->key + ht->keySize); + } + } + + return NULL; +} + +void +ht_dump_distribution(HashTable ht) +{ + int c; + int numBuckets = 1 << ht->bucketBits; + for (c = 0; c < numBuckets; ++c) { + BucketPtr it; + int n = 0; + + xorg_list_for_each_entry(it, &ht->buckets[c], l) { + ++n; + } + printf("%d: %d\n", c, n); + } +} + +/* Picked the function from http://burtleburtle.net/bob/hash/doobs.html by + Bob Jenkins, which is released in public domain */ +static CARD32 +one_at_a_time_hash(const void *data, int len) +{ + CARD32 hash; + int i; + const char *key = data; + for (hash=0, i=0; i<len; ++i) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; +} + +unsigned +ht_generic_hash(void *cdata, const void *ptr, int numBits) +{ + HtGenericHashSetupPtr setup = cdata; + return one_at_a_time_hash(ptr, setup->keySize) & ~((~0) << numBits); +} + +int +ht_generic_compare(void *cdata, const void *l, const void *r) +{ + HtGenericHashSetupPtr setup = cdata; + return memcmp(l, r, setup->keySize); +} + +unsigned +ht_resourceid_hash(void * cdata, const void * data, int numBits) +{ + const XID* idPtr = data; + XID id = *idPtr & RESOURCE_ID_MASK; + (void) cdata; + return HashResourceID(id, numBits); +} + +int +ht_resourceid_compare(void* cdata, const void* a, const void* b) +{ + const XID* xa = a; + const XID* xb = b; + (void) cdata; + return + *xa < *xb ? -1 : + *xa > *xb ? 1 : + 0; +} + +void +ht_dump_contents(HashTable ht, + void (*print_key)(void *opaque, void *key), + void (*print_value)(void *opaque, void *value), + void* opaque) +{ + int c; + int numBuckets = 1 << ht->bucketBits; + for (c = 0; c < numBuckets; ++c) { + BucketPtr it; + int n = 0; + + printf("%d: ", c); + xorg_list_for_each_entry(it, &ht->buckets[c], l) { + if (n > 0) { + printf(", "); + } + print_key(opaque, it->key); + printf("->"); + print_value(opaque, it->data); + ++n; + } + printf("\n"); + } +} diff --git a/Xext/hashtable.h b/Xext/hashtable.h new file mode 100644 index 000000000..5d1598425 --- /dev/null +++ b/Xext/hashtable.h @@ -0,0 +1,137 @@ +#ifndef HASHTABLE_H +#define HASHTABLE_H 1 + +#include <dix-config.h> +#include <X11/Xfuncproto.h> +#include <X11/Xdefs.h> +#include "list.h" + +/** @brief A hashing function. + + @param[in/out] cdata Opaque data that can be passed to HtInit that will + eventually end up here + @param[in] ptr The data to be hashed. The size of the data, if + needed, can be configured via a record that can be + passed via cdata. + @param[in] numBits The number of bits this hash needs to have in the + resulting hash + + @return A numBits-bit hash of the data +*/ +typedef unsigned (*HashFunc)(void * cdata, const void * ptr, int numBits); + +/** @brief A comparison function for hashed keys. + + @param[in/out] cdata Opaque data that ca be passed to Htinit that will + eventually end up here + @param[in] l The left side data to be compared + @param[in] r The right side data to be compared + + @return -1 if l < r, 0 if l == r, 1 if l > r +*/ +typedef int (*HashCompareFunc)(void * cdata, const void * l, const void * r); + +struct HashTableRec; + +typedef struct HashTableRec *HashTable; + +/** @brief A configuration for HtGenericHash */ +typedef struct { + int keySize; +} HtGenericHashSetupRec, *HtGenericHashSetupPtr; + +/** @brief ht_create initalizes a hash table for a certain hash table + configuration + + @param[out] ht The hash table structure to initialize + @param[in] keySize The key size in bytes + @param[in] dataSize The data size in bytes + @param[in] hash The hash function to use for hashing keys + @param[in] compare The comparison function for hashing keys + @param[in] cdata Opaque data that will be passed to hash and + comparison functions +*/ +extern _X_EXPORT HashTable ht_create(int keySize, + int dataSize, + HashFunc hash, + HashCompareFunc compare, + pointer cdata); +/** @brief HtDestruct deinitializes the structure. It does not free the + memory allocated to HashTableRec +*/ +extern _X_EXPORT void ht_destroy(HashTable ht); + +/** @brief Adds a new key to the hash table. The key will be copied + and a pointer to the value will be returned. The data will + be initialized with zeroes. + + @param[in/out] ht The hash table + @param[key] key The key. The contents of the key will be copied. + + @return On error NULL is returned, otherwise a pointer to the data + associated with the newly inserted key. + + @note If dataSize is 0, a pointer to the end of the key may be returned + to avoid returning NULL. Obviously the data pointed cannot be + modified, as implied by dataSize being 0. +*/ +extern _X_EXPORT pointer ht_add(HashTable ht, pointer key); + +/** @brief Removes a key from the hash table along with its + associated data, which will be free'd. +*/ +extern _X_EXPORT void ht_remove(HashTable ht, pointer key); + +/** @brief Finds the associated data of a key from the hash table. + + @return If the key cannot be found, the function returns NULL. + Otherwise it returns a pointer to the data associated + with the key. + + @note If dataSize == 0, this function may return NULL + even if the key has been inserted! If dataSize == NULL, + use HtMember instead to determine if a key has been + inserted. +*/ +extern _X_EXPORT pointer ht_find(HashTable ht, pointer key); + +/** @brief A generic hash function */ +extern _X_EXPORT unsigned ht_generic_hash(void *cdata, + const void *ptr, + int numBits); + +/** @brief A generic comparison function. It compares data byte-wise. */ +extern _X_EXPORT int ht_generic_compare(void *cdata, + const void *l, + const void *r); + +/** @brief A debugging function that dumps the distribution of the + hash table: for each bucket, list the number of elements + contained within. */ +extern _X_EXPORT void ht_dump_distribution(HashTable ht); + +/** @brief A debugging function that dumps the contents of the hash + table: for each bucket, list the elements contained + within. */ +extern _X_EXPORT void ht_dump_contents(HashTable ht, + void (*print_key)(void *opaque, void *key), + void (*print_value)(void *opaque, void *value), + void* opaque); + +/** @brief A hashing function to be used for hashing resource IDs when + used with HashTables. It makes no use of cdata, so that can + be NULL. It uses HashXID underneath, and should HashXID be + unable to hash the value, it switches into using the generic + hash function. */ +extern _X_EXPORT unsigned ht_resourceid_hash(void *cdata, + const void * data, + int numBits); + +/** @brief A comparison function to be used for comparing resource + IDs when used with HashTables. It makes no use of cdata, + so that can be NULL. */ +extern _X_EXPORT int ht_resourceid_compare(void *cdata, + const void *a, + const void *b); + +#endif // HASHTABLE_H diff --git a/Xext/xres.c b/Xext/xres.c index 9d89b6550..ecef0c032 100644 --- a/Xext/xres.c +++ b/Xext/xres.c @@ -10,6 +10,7 @@ #include <string.h> #include <X11/X.h> #include <X11/Xproto.h> +#include <assert.h> #include "misc.h" #include "os.h" #include "dixstruct.h" @@ -22,6 +23,169 @@ #include "gcstruct.h" #include "modinit.h" #include "protocol-versions.h" +#include "client.h" +#include "list.h" +#include "misc.h" +#include <string.h> +#include "hashtable.h" +#include "picturestr.h" +#include "compint.h" + +/** @brief Holds fragments of responses for ConstructClientIds. + * + * note: there is no consideration for data alignment */ +typedef struct { + struct xorg_list l; + int bytes; + /* data follows */ +} FragmentList; + +#define FRAGMENT_DATA(ptr) ((void*) ((char*) (ptr) + sizeof(FragmentList))) + +/** @brief Holds structure for the generated response to + ProcXResQueryClientIds; used by ConstructClientId* -functions */ +typedef struct { + int numIds; + int resultBytes; + struct xorg_list response; + int sentClientMasks[MAXCLIENTS]; +} ConstructClientIdCtx; + +/** @brief Holds the structure for information required to + generate the response to XResQueryResourceBytes. In addition + to response it contains information on the query as well, + as well as some volatile information required by a few + functions that cannot take that information directly + via a parameter, as they are called via already-existing + higher order functions. */ +typedef struct { + ClientPtr sendClient; + int numSizes; + int resultBytes; + struct xorg_list response; + int status; + long numSpecs; + xXResResourceIdSpec *specs; + HashTable visitedResources; + + /* Used by AddSubResourceSizeSpec when AddResourceSizeValue is + handling crossreferences */ + HashTable visitedSubResources; + + /* used when ConstructResourceBytesCtx is passed to + AddResourceSizeValue2 via FindClientResourcesByType */ + RESTYPE resType; + + /* used when ConstructResourceBytesCtx is passed to + AddResourceSizeValueByResource from ConstructResourceBytesByResource */ + xXResResourceIdSpec *curSpec; + + /** Used when iterating through a single resource's subresources + + @see AddSubResourceSizeSpec */ + xXResResourceSizeValue *sizeValue; +} ConstructResourceBytesCtx; + +/** @brief Allocate and add a sequence of bytes at the end of a fragment list. + Call DestroyFragments to release the list. + + @param frags A pointer to head of an initialized linked list + @param bytes Number of bytes to allocate + @return Returns a pointer to the allocated non-zeroed region + that is to be filled by the caller. On error (out of memory) + returns NULL and makes no changes to the list. +*/ +static void * +AddFragment(struct xorg_list *frags, int bytes) +{ + FragmentList *f = malloc(sizeof(FragmentList) + bytes); + if (!f) { + return NULL; + } else { + f->bytes = bytes; + xorg_list_add(&f->l, frags->prev); + return (char*) f + sizeof(*f); + } +} + +/** @brief Sends all fragments in the list to the client. Does not + free anything. + + @param client The client to send the fragments to + @param frags The head of the list of fragments +*/ +static void +WriteFragmentsToClient(ClientPtr client, struct xorg_list *frags) +{ + FragmentList *it; + xorg_list_for_each_entry(it, frags, l) { + WriteToClient(client, it->bytes, (char*) it + sizeof(*it)); + } +} + +/** @brief Frees a list of fragments. Does not free() root node. + + @param frags The head of the list of fragments +*/ +static void +DestroyFragments(struct xorg_list *frags) +{ + FragmentList *it, *tmp; + xorg_list_for_each_entry_safe(it, tmp, frags, l) { + xorg_list_del(&it->l); + free(it); + } +} + +/** @brief Constructs a context record for ConstructClientId* functions + to use */ +static void +InitConstructClientIdCtx(ConstructClientIdCtx *ctx) +{ + ctx->numIds = 0; + ctx->resultBytes = 0; + xorg_list_init(&ctx->response); + memset(ctx->sentClientMasks, 0, sizeof(ctx->sentClientMasks)); +} + +/** @brief Destroys a context record, releases all memory (except the storage + for *ctx itself) */ +static void +DestroyConstructClientIdCtx(ConstructClientIdCtx *ctx) +{ + DestroyFragments(&ctx->response); +} + +static Bool +InitConstructResourceBytesCtx(ConstructResourceBytesCtx *ctx, + ClientPtr sendClient, + long numSpecs, + xXResResourceIdSpec *specs) +{ + ctx->sendClient = sendClient; + ctx->numSizes = 0; + ctx->resultBytes = 0; + xorg_list_init(&ctx->response); + ctx->status = Success; + ctx->numSpecs = numSpecs; + ctx->specs = specs; + ctx->visitedResources = ht_create(sizeof(XID), 0, + ht_resourceid_hash, ht_resourceid_compare, + NULL); + + if (!ctx->visitedResources) { + return FALSE; + } else { + return TRUE; + } +} + +static void +DestroyConstructResourceBytesCtx(ConstructResourceBytesCtx *ctx) +{ + DestroyFragments(&ctx->response); + ht_destroy(ctx->visitedResources); +} static int ProcXResQueryVersion(ClientPtr client) @@ -195,6 +359,17 @@ ResGetApproxPixmapBytes(PixmapPtr pix) } static void +ResFindResourcePixmaps(pointer value, XID id, RESTYPE type, pointer cdata) +{ + SizeType sizeFunc = GetResourceTypeSizeFunc(type); + ResourceSizeRec size = { 0, 0, 0 }; + unsigned long *bytes = cdata; + + sizeFunc(value, id, &size); + *bytes += size.pixmapRefSize; +} + +static void ResFindPixmaps(pointer value, XID id, pointer cdata) { unsigned long *bytes = (unsigned long *) cdata; @@ -229,6 +404,22 @@ ResFindGCPixmaps(pointer value, XID id, pointer cdata) *bytes += ResGetApproxPixmapBytes(pGC->tile.pixmap); } +static void +ResFindPicturePixmaps(pointer value, XID id, pointer cdata) +{ +#ifdef RENDER + ResFindResourcePixmaps(value, id, PictureType, cdata); +#endif +} + +static void +ResFindCompositeClientWindowPixmaps (pointer value, XID id, pointer cdata) +{ +#ifdef COMPOSITE + ResFindResourcePixmaps(value, id, CompositeClientWindowType, cdata); +#endif +} + static int ProcXResQueryClientPixmapBytes(ClientPtr client) { @@ -263,8 +454,18 @@ ProcXResQueryClientPixmapBytes(ClientPtr client) FindClientResourcesByType(clients[clientID], RT_GC, ResFindGCPixmaps, (pointer) (&bytes)); +#ifdef RENDER + /* Render extension picture pixmaps. */ + FindClientResourcesByType(clients[clientID], PictureType, + ResFindPicturePixmaps, + (pointer)(&bytes)); +#endif + #ifdef COMPOSITE - /* FIXME: include composite pixmaps too */ + /* Composite extension client window pixmaps. */ + FindClientResourcesByType(clients[clientID], CompositeClientWindowType, + ResFindCompositeClientWindowPixmaps, + (pointer)(&bytes)); #endif rep.type = X_Reply; @@ -288,6 +489,584 @@ ProcXResQueryClientPixmapBytes(ClientPtr client) return Success; } +/** @brief Finds out if a client's information need to be put into the + response; marks client having been handled, if that is the case. + + @param client The client to send information about + @param mask The request mask (0 to send everything, otherwise a + bitmask of X_XRes*Mask) + @param ctx The context record that tells which clients and id types + have been already handled + @param sendMask Which id type are we now considering. One of X_XRes*Mask. + + @return Returns TRUE if the client information needs to be on the + response, otherwise FALSE. +*/ +static Bool +WillConstructMask(ClientPtr client, CARD32 mask, + ConstructClientIdCtx *ctx, int sendMask) +{ + if ((!mask || (mask & sendMask)) + && !(ctx->sentClientMasks[client->index] & sendMask)) { + ctx->sentClientMasks[client->index] |= sendMask; + return TRUE; + } else { + return FALSE; + } +} + +/** @brief Constructs a response about a single client, based on a certain + client id spec + + @param sendClient Which client wishes to receive this answer. Used for + byte endianess. + @param client Which client are we considering. + @param mask The client id spec mask indicating which information + we want about this client. + @param ctx The context record containing the constructed response + and information on which clients and masks have been + already handled. + + @return Return TRUE if everything went OK, otherwise FALSE which indicates + a memory allocation problem. +*/ +static Bool +ConstructClientIdValue(ClientPtr sendClient, ClientPtr client, CARD32 mask, + ConstructClientIdCtx *ctx) +{ + xXResClientIdValue rep; + + rep.spec.client = client->clientAsMask; + if (client->swapped) { + swapl (&rep.spec.client); + } + + if (WillConstructMask(client, mask, ctx, X_XResClientXIDMask)) { + void *ptr = AddFragment(&ctx->response, sizeof(rep)); + if (!ptr) { + return FALSE; + } + + rep.spec.mask = X_XResClientXIDMask; + rep.length = 0; + if (sendClient->swapped) { + swapl (&rep.spec.mask); + /* swapl (&rep.length, n); - not required for rep.length = 0 */ + } + + memcpy(ptr, &rep, sizeof(rep)); + + ctx->resultBytes += sizeof(rep); + ++ctx->numIds; + } + if (WillConstructMask(client, mask, ctx, X_XResLocalClientPIDMask)) { + pid_t pid = GetClientPid(client); + + if (pid != -1) { + void *ptr = AddFragment(&ctx->response, + sizeof(rep) + sizeof(CARD32)); + CARD32 *value = (void*) ((char*) ptr + sizeof(rep)); + + if (!ptr) { + return FALSE; + } + + rep.spec.mask = X_XResLocalClientPIDMask; + rep.length = 4; + + if (sendClient->swapped) { + swapl (&rep.spec.mask); + swapl (&rep.length); + } + + if (sendClient->swapped) { + swapl (value); + } + memcpy(ptr, &rep, sizeof(rep)); + *value = pid; + + ctx->resultBytes += sizeof(rep) + sizeof(CARD32); + ++ctx->numIds; + } + } + + /* memory allocation errors earlier may return with FALSE */ + return TRUE; +} + +/** @brief Constructs a response about all clients, based on a client id specs + + @param client Which client which we are constructing the response for. + @param numSpecs Number of client id specs in specs + @param specs Client id specs + + @return Return Success if everything went OK, otherwise a Bad* (currently + BadAlloc or BadValue) +*/ +static int +ConstructClientIds(ClientPtr client, + int numSpecs, xXResClientIdSpec* specs, + ConstructClientIdCtx *ctx) +{ + int specIdx; + + for (specIdx = 0; specIdx < numSpecs; ++specIdx) { + if (specs[specIdx].client == 0) { + int c; + for (c = 0; c < currentMaxClients; ++c) { + if (clients[c]) { + if (!ConstructClientIdValue(client, clients[c], + specs[specIdx].mask, ctx)) { + return BadAlloc; + } + } + } + } else { + int clientID = CLIENT_ID(specs[specIdx].client); + + if ((clientID < currentMaxClients) && clients[clientID]) { + if (!ConstructClientIdValue(client, clients[clientID], + specs[specIdx].mask, ctx)) { + return BadAlloc; + } + } + } + } + + /* memory allocation errors earlier may return with BadAlloc */ + return Success; +} + +/** @brief Response to XResQueryClientIds request introduced in XResProto v1.2 + + @param client Which client which we are constructing the response for. + + @return Returns the value returned from ConstructClientIds with the same + semantics +*/ +static int +ProcXResQueryClientIds (ClientPtr client) +{ + REQUEST(xXResQueryClientIdsReq); + + xXResQueryClientIdsReply rep; + xXResClientIdSpec *specs = (void*) ((char*) stuff + sizeof(*stuff)); + int rc; + ConstructClientIdCtx ctx; + + InitConstructClientIdCtx(&ctx); + + REQUEST_AT_LEAST_SIZE(xXResQueryClientIdsReq); + REQUEST_FIXED_SIZE(xXResQueryClientIdsReq, + stuff->numSpecs * sizeof(specs[0])); + + rc = ConstructClientIds(client, stuff->numSpecs, specs, &ctx); + + if (rc == Success) { + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + + assert((ctx.resultBytes & 3) == 0); + rep.length = bytes_to_int32(ctx.resultBytes); + rep.numIds = ctx.numIds; + + if (client->swapped) { + swaps (&rep.sequenceNumber); + swapl (&rep.length); + swapl (&rep.numIds); + } + + WriteToClient(client,sizeof(rep),(char*)&rep); + WriteFragmentsToClient(client, &ctx.response); + } + + DestroyConstructClientIdCtx(&ctx); + + return rc; +} + +/** @brief Swaps xXResResourceIdSpec endianess */ +static void +SwapXResResourceIdSpec(xXResResourceIdSpec *spec) +{ + swapl(&spec->resource); + swapl(&spec->type); +} + +/** @brief Swaps xXResResourceSizeSpec endianess */ +static void +SwapXResResourceSizeSpec(xXResResourceSizeSpec *size) +{ + SwapXResResourceIdSpec(&size->spec); + swapl(&size->bytes); + swapl(&size->refCount); + swapl(&size->useCount); +} + +/** @brief Swaps xXResResourceSizeValue endianess */ +static void +SwapXResResourceSizeValue(xXResResourceSizeValue *rep) +{ + SwapXResResourceSizeSpec(&rep->size); + swapl(&rep->numCrossReferences); +} + +/** @brief Swaps the response bytes */ +static void +SwapXResQueryResourceBytes(struct xorg_list *response) +{ + struct xorg_list *it = response->next; + int c; + + while (it != response) { + xXResResourceSizeValue *value = FRAGMENT_DATA(it); + it = it->next; + for (c = 0; c < value->numCrossReferences; ++c) { + xXResResourceSizeSpec *spec = FRAGMENT_DATA(it); + SwapXResResourceSizeSpec(spec); + it = it->next; + } + SwapXResResourceSizeValue(value); + } +} + +/** @brief Adds xXResResourceSizeSpec describing a resource's size into + the buffer contained in the context. The resource is considered + to be a subresource. + + @see AddResourceSizeValue + + @param[in] value The X resource object on which to add information + about to the buffer + @param[in] id The ID of the X resource + @param[in] type The type of the X resource + @param[in/out] cdata The context object of type ConstructResourceBytesCtx. + Void pointer type is used here to satisfy the type + FindRes +*/ +static void +AddSubResourceSizeSpec(pointer value, + XID id, + RESTYPE type, + pointer cdata) +{ + ConstructResourceBytesCtx *ctx = cdata; + + if (ctx->status == Success) { + xXResResourceSizeSpec **prevCrossRef = + ht_find(ctx->visitedSubResources, &value); + if (!prevCrossRef) { + Bool ok = TRUE; + xXResResourceSizeSpec *crossRef = + AddFragment(&ctx->response, sizeof(xXResResourceSizeSpec)); + ok = ok && crossRef != NULL; + if (ok) { + xXResResourceSizeSpec **p; + p = ht_add(ctx->visitedSubResources, &value); + if (!p) { + ok = FALSE; + } else { + *p = crossRef; + } + } + if (!ok) { + ctx->status = BadAlloc; + } else { + SizeType sizeFunc = GetResourceTypeSizeFunc(type); + ResourceSizeRec size = { 0, 0, 0 }; + sizeFunc(value, id, &size); + + crossRef->spec.resource = id; + crossRef->spec.type = type; + crossRef->bytes = size.resourceSize; + crossRef->refCount = size.refCnt; + crossRef->useCount = 1; + + ++ctx->sizeValue->numCrossReferences; + + ctx->resultBytes += sizeof(*crossRef); + } + } else { + /* if we have visited the subresource earlier (from current parent + resource), just increase its use count by one */ + ++(*prevCrossRef)->useCount; + } + } +} + +/** @brief Adds xXResResourceSizeValue describing a resource's size into + the buffer contained in the context. In addition, the + subresources are iterated and added as xXResResourceSizeSpec's + by using AddSubResourceSizeSpec + + @see AddSubResourceSizeSpec + + @param[in] value The X resource object on which to add information + about to the buffer + @param[in] id The ID of the X resource + @param[in] type The type of the X resource + @param[in/out] cdata The context object of type ConstructResourceBytesCtx. + Void pointer type is used here to satisfy the type + FindRes +*/ +static void +AddResourceSizeValue(pointer ptr, XID id, RESTYPE type, pointer cdata) +{ + ConstructResourceBytesCtx *ctx = cdata; + if (ctx->status == Success && + !ht_find(ctx->visitedResources, &id)) { + Bool ok = TRUE; + HashTable ht; + HtGenericHashSetupRec htSetup = { + .keySize = sizeof(void*) + }; + + /* it doesn't matter that we don't undo the work done here + * immediately. All but ht_init will be undone at the end + * of the request and there can happen no failure after + * ht_init, so we don't need to clean it up here in any + * special way */ + + xXResResourceSizeValue *value = + AddFragment(&ctx->response, sizeof(xXResResourceSizeValue)); + if (!value) { + ok = FALSE; + } + ok = ok && ht_add(ctx->visitedResources, &id); + if (ok) { + ht = ht_create(htSetup.keySize, + sizeof(xXResResourceSizeSpec*), + ht_generic_hash, ht_generic_compare, + &htSetup); + ok = ok && ht; + } + + if (!ok) { + ctx->status = BadAlloc; + } else { + SizeType sizeFunc = GetResourceTypeSizeFunc(type); + ResourceSizeRec size = { 0, 0, 0 }; + + sizeFunc(ptr, id, &size); + + value->size.spec.resource = id; + value->size.spec.type = type; + value->size.bytes = size.resourceSize; + value->size.refCount = size.refCnt; + value->size.useCount = 1; + value->numCrossReferences = 0; + + ctx->sizeValue = value; + ctx->visitedSubResources = ht; + FindSubResources(ptr, type, AddSubResourceSizeSpec, ctx); + ctx->visitedSubResources = NULL; + ctx->sizeValue = NULL; + + ctx->resultBytes += sizeof(*value); + ++ctx->numSizes; + + ht_destroy(ht); + } + } +} + +/** @brief A variant of AddResourceSizeValue that passes the resource type + through the context object to satisfy the type FindResType + + @see AddResourceSizeValue + + @param[in] ptr The resource + @param[in] id The resource ID + @param[in/out] cdata The context object that contains the resource type +*/ +static void +AddResourceSizeValueWithResType(pointer ptr, XID id, pointer cdata) +{ + ConstructResourceBytesCtx *ctx = cdata; + AddResourceSizeValue(ptr, id, ctx->resType, cdata); +} + +/** @brief Adds the information of a resource into the buffer if it matches + the match condition. + + @see AddResourceSizeValue + + @param[in] ptr The resource + @param[in] id The resource ID + @param[in] type The resource type + @param[in/out] cdata The context object as a void pointer to satisfy the + type FindAllRes +*/ +static void +AddResourceSizeValueByResource(pointer ptr, XID id, RESTYPE type, pointer cdata) +{ + ConstructResourceBytesCtx *ctx = cdata; + xXResResourceIdSpec *spec = ctx->curSpec; + + if ((!spec->type || spec->type == type) && + (!spec->resource || spec->resource == id)) { + AddResourceSizeValue(ptr, id, type, ctx); + } +} + +/** @brief Add all resources of the client into the result buffer + disregarding all those specifications that specify the + resource by its ID. Those are handled by + ConstructResourceBytesByResource + + @see ConstructResourceBytesByResource + + @param[in] aboutClient Which client is being considered + @param[in/out] ctx The context that contains the resource id + specifications as well as the result buffer +*/ +static void +ConstructClientResourceBytes(ClientPtr aboutClient, + ConstructResourceBytesCtx *ctx) +{ + int specIdx; + for (specIdx = 0; specIdx < ctx->numSpecs; ++specIdx) { + xXResResourceIdSpec* spec = ctx->specs + specIdx; + if (spec->resource) { + /* these specs are handled elsewhere */ + } else if (spec->type) { + ctx->resType = spec->type; + FindClientResourcesByType(aboutClient, spec->type, + AddResourceSizeValueWithResType, ctx); + } else { + FindAllClientResources(aboutClient, AddResourceSizeValue, ctx); + } + } +} + +/** @brief Add the sizes of all such resources that can are specified by + their ID in the resource id specification. The scan can + by limited to a client with the aboutClient parameter + + @see ConstructResourceBytesByResource + + @param[in] aboutClient Which client is being considered. This may be None + to mean all clients. + @param[in/out] ctx The context that contains the resource id + specifications as well as the result buffer. In + addition this function uses the curSpec field to + keep a pointer to the current resource id + specification in it, which can be used by + AddResourceSizeValueByResource . +*/ +static void +ConstructResourceBytesByResource(XID aboutClient, ConstructResourceBytesCtx *ctx) +{ + int specIdx; + for (specIdx = 0; specIdx < ctx->numSpecs; ++specIdx) { + xXResResourceIdSpec *spec = ctx->specs + specIdx; + if (spec->resource) { + int cid = CLIENT_ID(spec->resource); + if (cid < currentMaxClients && + (aboutClient == None || cid == aboutClient)) { + ClientPtr client = clients[cid]; + if (client) { + ctx->curSpec = spec; + FindAllClientResources(client, + AddResourceSizeValueByResource, + ctx); + } + } + } + } +} + +/** @brief Build the resource size response for the given client + (or all if not specified) per the parameters set up + in the context object. + + @param[in] aboutClient Which client to consider or None for all clients + @param[in/out] ctx The context object that contains the request as well + as the response buffer. +*/ +static int +ConstructResourceBytes(XID aboutClient, + ConstructResourceBytesCtx *ctx) +{ + if (aboutClient) { + int clientIdx = CLIENT_ID(aboutClient); + ClientPtr client = NullClient; + + if ((clientIdx >= currentMaxClients) || !clients[clientIdx]) { + ctx->sendClient->errorValue = aboutClient; + return BadValue; + } + + client = clients[clientIdx]; + + ConstructClientResourceBytes(client, ctx); + ConstructResourceBytesByResource(aboutClient, ctx); + } else { + int clientIdx; + + ConstructClientResourceBytes(NULL, ctx); + + for (clientIdx = 0; clientIdx < currentMaxClients; ++clientIdx) { + ClientPtr client = clients[clientIdx]; + + if (client) { + ConstructClientResourceBytes(client, ctx); + } + } + + ConstructResourceBytesByResource(None, ctx); + } + + + return ctx->status; +} + +/** @brief Implements the XResQueryResourceBytes of XResProto v1.2 */ +static int +ProcXResQueryResourceBytes (ClientPtr client) +{ + REQUEST(xXResQueryResourceBytesReq); + + xXResQueryResourceBytesReply rep; + int rc; + ConstructResourceBytesCtx ctx; + + REQUEST_AT_LEAST_SIZE(xXResQueryResourceBytesReq); + REQUEST_FIXED_SIZE(xXResQueryResourceBytesReq, + stuff->numSpecs * sizeof(ctx.specs[0])); + + if (!InitConstructResourceBytesCtx(&ctx, client, + stuff->numSpecs, + (void*) ((char*) stuff + + sz_xXResQueryResourceBytesReq))) { + return BadAlloc; + } + + rc = ConstructResourceBytes(stuff->client, &ctx); + + if (rc == Success) { + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.numSizes = ctx.numSizes; + rep.length = bytes_to_int32(ctx.resultBytes); + + if (client->swapped) { + swaps (&rep.sequenceNumber); + swapl (&rep.length); + swapl (&rep.numSizes); + + SwapXResQueryResourceBytes(&ctx.response); + } + + WriteToClient(client,sizeof(rep),(char*)&rep); + WriteFragmentsToClient(client, &ctx.response); + } + + DestroyConstructResourceBytesCtx(&ctx); + + return rc; +} + static int ProcResDispatch(ClientPtr client) { @@ -301,8 +1080,11 @@ ProcResDispatch(ClientPtr client) return ProcXResQueryClientResources(client); case X_XResQueryClientPixmapBytes: return ProcXResQueryClientPixmapBytes(client); - default: - break; + case X_XResQueryClientIds: + return ProcXResQueryClientIds(client); + case X_XResQueryResourceBytes: + return ProcXResQueryResourceBytes(client); + default: break; } return BadRequest; @@ -335,7 +1117,39 @@ SProcXResQueryClientPixmapBytes(ClientPtr client) } static int -SProcResDispatch(ClientPtr client) +SProcXResQueryClientIds (ClientPtr client) +{ + REQUEST(xXResQueryClientIdsReq); + + REQUEST_AT_LEAST_SIZE (xXResQueryClientIdsReq); + swapl(&stuff->numSpecs); + return ProcXResQueryClientIds(client); +} + +/** @brief Implements the XResQueryResourceBytes of XResProto v1.2. + This variant byteswaps request contents before issuing the + rest of the work to ProcXResQueryResourceBytes */ +static int +SProcXResQueryResourceBytes (ClientPtr client) +{ + REQUEST(xXResQueryResourceBytesReq); + int c; + xXResResourceIdSpec *specs = (void*) ((char*) stuff + sizeof(*stuff)); + + swapl(&stuff->numSpecs); + REQUEST_AT_LEAST_SIZE(xXResQueryResourceBytesReq); + REQUEST_FIXED_SIZE(xXResQueryResourceBytesReq, + stuff->numSpecs * sizeof(specs[0])); + + for (c = 0; c < stuff->numSpecs; ++c) { + SwapXResResourceIdSpec(specs + c); + } + + return ProcXResQueryResourceBytes(client); +} + +static int +SProcResDispatch (ClientPtr client) { REQUEST(xReq); swaps(&stuff->length); @@ -349,8 +1163,11 @@ SProcResDispatch(ClientPtr client) return SProcXResQueryClientResources(client); case X_XResQueryClientPixmapBytes: return SProcXResQueryClientPixmapBytes(client); - default: - break; + case X_XResQueryClientIds: + return SProcXResQueryClientIds(client); + case X_XResQueryResourceBytes: + return SProcXResQueryResourceBytes(client); + default: break; } return BadRequest; diff --git a/composite/compext.c b/composite/compext.c index 940eed1fc..1d4d8bf97 100644 --- a/composite/compext.c +++ b/composite/compext.c @@ -497,6 +497,28 @@ SProcCompositeDispatch(ClientPtr client) return BadRequest; } +/** @see GetDefaultBytes */ +static void +GetCompositeClientWindowBytes(pointer value, XID id, ResourceSizePtr size) +{ + WindowPtr window = value; + + /* Currently only pixmap bytes are reported to clients. */ + size->resourceSize = 0; + + /* Calculate pixmap reference sizes. */ + size->pixmapRefSize = 0; + if (window->redirectDraw != RedirectDrawNone) + { + SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP); + ResourceSizeRec pixmapSize = { 0, 0 }; + ScreenPtr screen = window->drawable.pScreen; + PixmapPtr pixmap = screen->GetWindowPixmap(window); + pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize); + size->pixmapRefSize += pixmapSize.pixmapRefSize; + } +} + void CompositeExtensionInit(void) { @@ -529,6 +551,9 @@ CompositeExtensionInit(void) if (!CompositeClientWindowType) return; + SetResourceTypeSizeFunc(CompositeClientWindowType, + GetCompositeClientWindowBytes); + CompositeClientSubwindowsType = CreateNewResourceType (FreeCompositeClientSubwindows, "CompositeClientSubwindows"); if (!CompositeClientSubwindowsType) diff --git a/configure.ac b/configure.ac index 65d29f206..ea1d286a1 100644 --- a/configure.ac +++ b/configure.ac @@ -759,7 +759,7 @@ VIDEOPROTO="videoproto" COMPOSITEPROTO="compositeproto >= 0.4" RECORDPROTO="recordproto >= 1.13.99.1" SCRNSAVERPROTO="scrnsaverproto >= 1.1" -RESOURCEPROTO="resourceproto" +RESOURCEPROTO="resourceproto >= 1.2.0" DRIPROTO="xf86driproto >= 2.1.0" DRI2PROTO="dri2proto >= 2.6" XINERAMAPROTO="xineramaproto" diff --git a/dix/resource.c b/dix/resource.c index 89d077642..2aafa343c 100644 --- a/dix/resource.c +++ b/dix/resource.c @@ -141,6 +141,7 @@ Equipment Corporation. #include "xace.h" #include <assert.h> #include "registry.h" +#include "gcstruct.h" #ifdef XSERVER_DTRACE #include <sys/types.h> @@ -182,50 +183,313 @@ RESTYPE TypeMask; struct ResourceType { DeleteType deleteFunc; + SizeType sizeFunc; + FindTypeSubResources findSubResFunc; int errorValue; }; +/** + * Used by all resources that don't specify a function to calculate + * resource size. Currently this is used for all resources with + * insignificant memory usage. + * + * @see GetResourceTypeSizeFunc, SetResourceTypeSizeFunc + * + * @param[in] value Pointer to resource object. + * + * @param[in] id Resource ID for the object. + * + * @param[out] size Fill all fields to zero to indicate that size of + * resource can't be determined. + */ +static void +GetDefaultBytes(pointer value, XID id, ResourceSizePtr size) +{ + size->resourceSize = 0; + size->pixmapRefSize = 0; + size->refCnt = 1; +} + +/** + * Used by all resources that don't specify a function to iterate + * through subresources. Currently this is used for all resources with + * insignificant memory usage. + * + * @see FindSubResources, SetResourceTypeFindSubResFunc + * + * @param[in] value Pointer to resource object. + * + * @param[in] func Function to call for each subresource. + + * @param[out] cdata Pointer to opaque data. + */ +static void +DefaultFindSubRes(pointer value, FindAllRes func, pointer cdata) +{ + /* do nothing */ +} + +/** + * Calculate drawable size in bytes. Reference counting is not taken + * into account. + * + * @param[in] drawable Pointer to a drawable. + * + * @return Estimate of total memory usage for the drawable. + */ +static unsigned long +GetDrawableBytes(DrawablePtr drawable) +{ + int bytes = 0; + + if (drawable) + { + int bytesPerPixel = drawable->bitsPerPixel >> 3; + int numberOfPixels = drawable->width * drawable->height; + bytes = numberOfPixels * bytesPerPixel; + } + + return bytes; +} + +/** + * Calculate pixmap size in bytes. Reference counting is taken into + * account. Any extra data attached by extensions and drivers is not + * taken into account. The purpose of this function is to estimate + * memory usage that can be attributed to single reference of the + * pixmap. + * + * @param[in] value Pointer to a pixmap. + * + * @param[in] id Resource ID of pixmap. If the pixmap hasn't been + * added as resource, just pass value->drawable.id. + * + * @param[out] size Estimate of memory usage attributed to a single + * pixmap reference. + */ +static void +GetPixmapBytes(pointer value, XID id, ResourceSizePtr size) +{ + PixmapPtr pixmap = value; + + size->resourceSize = 0; + size->pixmapRefSize = 0; + size->refCnt = pixmap->refcnt; + + if (pixmap && pixmap->refcnt) + { + DrawablePtr drawable = &pixmap->drawable; + size->resourceSize = GetDrawableBytes(drawable); + size->pixmapRefSize = size->resourceSize / pixmap->refcnt; + } +} + +/** + * Calculate window size in bytes. The purpose of this function is to + * estimate memory usage that can be attributed to all pixmap + * references of the window. + * + * @param[in] value Pointer to a window. + * + * @param[in] id Resource ID of window. + * + * @param[out] size Estimate of memory usage attributed to a all + * pixmap references of a window. + */ +static void +GetWindowBytes(pointer value, XID id, ResourceSizePtr size) +{ + SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP); + ResourceSizeRec pixmapSize = { 0, 0, 0 }; + WindowPtr window = value; + + /* Currently only pixmap bytes are reported to clients. */ + size->resourceSize = 0; + + /* Calculate pixmap reference sizes. */ + size->pixmapRefSize = 0; + + size->refCnt = 1; + + if (window->backgroundState == BackgroundPixmap) + { + PixmapPtr pixmap = window->background.pixmap; + pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize); + size->pixmapRefSize += pixmapSize.pixmapRefSize; + } + if (window->border.pixmap && !window->borderIsPixel) + { + PixmapPtr pixmap = window->border.pixmap; + pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize); + size->pixmapRefSize += pixmapSize.pixmapRefSize; + } +} + +/** + * Iterate through subresources of a window. The purpose of this + * function is to gather accurate information on what resources + * a resource uses. + * + * @note Currently only sub-pixmaps are iterated + * + * @param[in] value Pointer to a window + * + * @param[in] func Function to call with each subresource + * + * @param[out] cdata Pointer to opaque data + */ +static void +FindWindowSubRes(pointer value, FindAllRes func, pointer cdata) +{ + WindowPtr window = value; + + /* Currently only pixmap subresources are reported to clients. */ + + if (window->backgroundState == BackgroundPixmap) + { + PixmapPtr pixmap = window->background.pixmap; + func(window->background.pixmap, pixmap->drawable.id, RT_PIXMAP, cdata); + } + if (window->border.pixmap && !window->borderIsPixel) + { + PixmapPtr pixmap = window->border.pixmap; + func(window->background.pixmap, pixmap->drawable.id, RT_PIXMAP, cdata); + } +} + +/** + * Calculate graphics context size in bytes. The purpose of this + * function is to estimate memory usage that can be attributed to all + * pixmap references of the graphics context. + * + * @param[in] value Pointer to a graphics context. + * + * @param[in] id Resource ID of graphics context. + * + * @param[out] size Estimate of memory usage attributed to a all + * pixmap references of a graphics context. + */ +static void +GetGcBytes(pointer value, XID id, ResourceSizePtr size) +{ + SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP); + ResourceSizeRec pixmapSize = { 0, 0, 0 }; + GCPtr gc = value; + + /* Currently only pixmap bytes are reported to clients. */ + size->resourceSize = 0; + + /* Calculate pixmap reference sizes. */ + size->pixmapRefSize = 0; + + size->refCnt = 1; + if (gc->stipple) + { + PixmapPtr pixmap = gc->stipple; + pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize); + size->pixmapRefSize += pixmapSize.pixmapRefSize; + } + if (gc->tile.pixmap && !gc->tileIsPixel) + { + PixmapPtr pixmap = gc->tile.pixmap; + pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize); + size->pixmapRefSize += pixmapSize.pixmapRefSize; + } +} + +/** + * Iterate through subresources of a graphics context. The purpose of + * this function is to gather accurate information on what resources a + * resource uses. + * + * @note Currently only sub-pixmaps are iterated + * + * @param[in] value Pointer to a window + * + * @param[in] func Function to call with each subresource + * + * @param[out] cdata Pointer to opaque data + */ +static void +FindGCSubRes(pointer value, FindAllRes func, pointer cdata) +{ + GCPtr gc = value; + + /* Currently only pixmap subresources are reported to clients. */ + + if (gc->stipple) + { + PixmapPtr pixmap = gc->stipple; + func(pixmap, pixmap->drawable.id, RT_PIXMAP, cdata); + } + if (gc->tile.pixmap && !gc->tileIsPixel) + { + PixmapPtr pixmap = gc->tile.pixmap; + func(pixmap, pixmap->drawable.id, RT_PIXMAP, cdata); + } +} + static struct ResourceType *resourceTypes; static const struct ResourceType predefTypes[] = { [RT_NONE & (RC_LASTPREDEF - 1)] = { .deleteFunc = (DeleteType) NoopDDA, + .sizeFunc = GetDefaultBytes, + .findSubResFunc = DefaultFindSubRes, .errorValue = BadValue, }, [RT_WINDOW & (RC_LASTPREDEF - 1)] = { .deleteFunc = DeleteWindow, + .sizeFunc = GetWindowBytes, + .findSubResFunc = FindWindowSubRes, .errorValue = BadWindow, }, [RT_PIXMAP & (RC_LASTPREDEF - 1)] = { .deleteFunc = dixDestroyPixmap, + .sizeFunc = GetPixmapBytes, + .findSubResFunc = DefaultFindSubRes, .errorValue = BadPixmap, }, [RT_GC & (RC_LASTPREDEF - 1)] = { .deleteFunc = FreeGC, + .sizeFunc = GetGcBytes, + .findSubResFunc = FindGCSubRes, .errorValue = BadGC, }, [RT_FONT & (RC_LASTPREDEF - 1)] = { .deleteFunc = CloseFont, + .sizeFunc = GetDefaultBytes, + .findSubResFunc = DefaultFindSubRes, .errorValue = BadFont, }, [RT_CURSOR & (RC_LASTPREDEF - 1)] = { .deleteFunc = FreeCursor, + .sizeFunc = GetDefaultBytes, + .findSubResFunc = DefaultFindSubRes, .errorValue = BadCursor, }, [RT_COLORMAP & (RC_LASTPREDEF - 1)] = { .deleteFunc = FreeColormap, + .sizeFunc = GetDefaultBytes, + .findSubResFunc = DefaultFindSubRes, .errorValue = BadColor, }, [RT_CMAPENTRY & (RC_LASTPREDEF - 1)] = { .deleteFunc = FreeClientPixels, + .sizeFunc = GetDefaultBytes, + .findSubResFunc = DefaultFindSubRes, .errorValue = BadColor, }, [RT_OTHERCLIENT & (RC_LASTPREDEF - 1)] = { .deleteFunc = OtherClientGone, + .sizeFunc = GetDefaultBytes, + .findSubResFunc = DefaultFindSubRes, .errorValue = BadValue, }, [RT_PASSIVEGRAB & (RC_LASTPREDEF - 1)] = { .deleteFunc = DeletePassiveGrab, + .sizeFunc = GetDefaultBytes, + .findSubResFunc = DefaultFindSubRes, .errorValue = BadValue, }, }; @@ -256,6 +520,8 @@ CreateNewResourceType(DeleteType deleteFunc, const char *name) lastResourceType = next; resourceTypes = types; resourceTypes[next].deleteFunc = deleteFunc; + resourceTypes[next].sizeFunc = GetDefaultBytes; + resourceTypes[next].findSubResFunc = DefaultFindSubRes; resourceTypes[next].errorValue = BadValue; /* Called even if name is NULL, to remove any previous entry */ @@ -264,6 +530,57 @@ CreateNewResourceType(DeleteType deleteFunc, const char *name) return next; } +/** + * Get the function used to calculate resource size. Extensions and + * drivers need to be able to determine the current size calculation + * function if they want to wrap or override it. + * + * @param[in] type Resource type used in size calculations. + * + * @return Function to calculate the size of a single + * resource. + */ +SizeType +GetResourceTypeSizeFunc(RESTYPE type) +{ + return resourceTypes[type & TypeMask].sizeFunc; +} + +/** + * Override the default function that calculates resource size. For + * example, video driver knows better how to calculate pixmap memory + * usage and can therefore wrap or override size calculation for + * RT_PIXMAP. + * + * @param[in] type Resource type used in size calculations. + * + * @param[in] sizeFunc Function to calculate the size of a single + * resource. + */ +void +SetResourceTypeSizeFunc(RESTYPE type, SizeType sizeFunc) +{ + resourceTypes[type & TypeMask].sizeFunc = sizeFunc; +} + +/** + * Provide a function for iterating the subresources of a resource. + * This allows for example more accurate accounting of the (memory) + * resources consumed by a resource. + * + * @see FindSubResources + * + * @param[in] type Resource type used in size calculations. + * + * @param[in] sizeFunc Function to calculate the size of a single + * resource. + */ +void +SetResourceTypeFindSubResFunc(RESTYPE type, FindTypeSubResources findFunc) +{ + resourceTypes[type & TypeMask].findSubResFunc = findFunc; +} + void SetResourceTypeErrorValue(RESTYPE type, int errorValue) { @@ -326,25 +643,32 @@ InitClientResources(ClientPtr client) return TRUE; } -static int -Hash(int client, XID id) +int +HashResourceID(XID id, int numBits) { id &= RESOURCE_ID_MASK; - switch (clientTable[client].hashsize) { - case 6: - return ((int) (0x03F & (id ^ (id >> 6) ^ (id >> 12)))); - case 7: - return ((int) (0x07F & (id ^ (id >> 7) ^ (id >> 13)))); - case 8: - return ((int) (0x0FF & (id ^ (id >> 8) ^ (id >> 16)))); - case 9: - return ((int) (0x1FF & (id ^ (id >> 9)))); - case 10: - return ((int) (0x3FF & (id ^ (id >> 10)))); - case 11: - return ((int) (0x7FF & (id ^ (id >> 11)))); - } - return -1; + switch (numBits) + { + case 6: + return ((int)(0x03F & (id ^ (id>>6) ^ (id>>12)))); + case 7: + return ((int)(0x07F & (id ^ (id>>7) ^ (id>>13)))); + case 8: + return ((int)(0x0FF & (id ^ (id>>8) ^ (id>>16)))); + case 9: + return ((int)(0x1FF & (id ^ (id>>9)))); + case 10: + return ((int)(0x3FF & (id ^ (id>>10)))); + case 11: + return ((int)(0x7FF & (id ^ (id>>11)))); + } + if (numBits >= 11) + return ((int)(0x7FF & (id ^ (id>>11)))); + else + { + assert(numBits >= 0); + return id & ~((~0) << numBits); + } } static XID @@ -355,7 +679,7 @@ AvailableID(int client, XID id, XID maxid, XID goodid) if ((goodid >= id) && (goodid <= maxid)) return goodid; for (; id <= maxid; id++) { - res = clientTable[client].resources[Hash(client, id)]; + res = clientTable[client].resources[HashResourceID(id, clientTable[client].hashsize)]; while (res && (res->id != id)) res = res->next; if (!res) @@ -481,7 +805,7 @@ AddResource(XID id, RESTYPE type, pointer value) } if ((rrec->elements >= 4 * rrec->buckets) && (rrec->hashsize < MAXHASHSIZE)) RebuildTable(client); - head = &rrec->resources[Hash(client, id)]; + head = &rrec->resources[HashResourceID(id, clientTable[client].hashsize)]; res = malloc(sizeof(ResourceRec)); if (!res) { (*resourceTypes[type & TypeMask].deleteFunc) (value, id); @@ -529,7 +853,7 @@ RebuildTable(int client) for (res = *rptr; res; res = next) { next = res->next; res->next = NULL; - tptr = &tails[Hash(client, res->id)]; + tptr = &tails[HashResourceID(res->id, clientTable[client].hashsize)]; **tptr = res; *tptr = &res->next; } @@ -561,7 +885,7 @@ FreeResource(XID id, RESTYPE skipDeleteFuncType) int elements; if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets) { - head = &clientTable[cid].resources[Hash(cid, id)]; + head = &clientTable[cid].resources[HashResourceID(id, clientTable[cid].hashsize)]; eltptr = &clientTable[cid].elements; prev = head; @@ -595,7 +919,7 @@ FreeResourceByType(XID id, RESTYPE type, Bool skipFree) ResourcePtr *prev, *head; if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets) { - head = &clientTable[cid].resources[Hash(cid, id)]; + head = &clientTable[cid].resources[HashResourceID(id, clientTable[cid].hashsize)]; prev = head; while ((res = *prev)) { @@ -630,7 +954,7 @@ ChangeResourceValue(XID id, RESTYPE rtype, pointer value) ResourcePtr res; if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets) { - res = clientTable[cid].resources[Hash(cid, id)]; + res = clientTable[cid].resources[HashResourceID(id, clientTable[cid].hashsize)]; for (; res; res = res->next) if ((res->id == id) && (res->type == rtype)) { @@ -674,6 +998,15 @@ FindClientResourcesByType(ClientPtr client, } } +void FindSubResources(pointer resource, + RESTYPE type, + FindAllRes func, + pointer cdata) +{ + struct ResourceType rtype = resourceTypes[type & TypeMask]; + rtype.findSubResFunc(resource, func, cdata); +} + void FindAllClientResources(ClientPtr client, FindAllRes func, pointer cdata) { @@ -859,7 +1192,7 @@ dixLookupResourceByType(pointer *result, XID id, RESTYPE rtype, return BadImplementation; if ((cid < MAXCLIENTS) && clientTable[cid].buckets) { - res = clientTable[cid].resources[Hash(cid, id)]; + res = clientTable[cid].resources[HashResourceID(id, clientTable[cid].hashsize)]; for (; res; res = res->next) if (res->id == id && res->type == rtype) @@ -892,7 +1225,7 @@ dixLookupResourceByClass(pointer *result, XID id, RESTYPE rclass, *result = NULL; if ((cid < MAXCLIENTS) && clientTable[cid].buckets) { - res = clientTable[cid].resources[Hash(cid, id)]; + res = clientTable[cid].resources[HashResourceID(id, clientTable[cid].hashsize)]; for (; res; res = res->next) if (res->id == id && (res->type & rclass)) diff --git a/include/protocol-versions.h b/include/protocol-versions.h index 479ac2f77..cb8e213bb 100644 --- a/include/protocol-versions.h +++ b/include/protocol-versions.h @@ -135,7 +135,7 @@ /* Resource */ #define SERVER_XRES_MAJOR_VERSION 1 -#define SERVER_XRES_MINOR_VERSION 0 +#define SERVER_XRES_MINOR_VERSION 2 /* XvMC */ #define SERVER_XVMC_MAJOR_VERSION 1 diff --git a/include/resource.h b/include/resource.h index 068057013..19f46d108 100644 --- a/include/resource.h +++ b/include/resource.h @@ -152,12 +152,46 @@ typedef Bool (*FindComplexResType) (pointer /*value */ , XID /*id */ , pointer /*cdata */ ); +/* Structure for estimating resource memory usage. Memory usage + * consists of space allocated for the resource itself and of + * references to other resources. Currently the most important use for + * this structure is to estimate pixmap usage of different resources + * more accurately. */ +typedef struct { + /* Size of resource itself. Zero if not implemented. */ + unsigned long resourceSize; + /* Size attributed to pixmap references from the resource. */ + unsigned long pixmapRefSize; + /* Number of references to this resource; typically 1 */ + unsigned long refCnt; +} ResourceSizeRec, *ResourceSizePtr; + +typedef void (*SizeType)(pointer /*value*/, + XID /*id*/, + ResourceSizePtr /*size*/); + extern _X_EXPORT RESTYPE CreateNewResourceType(DeleteType /*deleteFunc */ , const char * /*name */ ); +typedef void (*FindTypeSubResources)(pointer /* value */, + FindAllRes /* func */, + pointer /* cdata */); + extern _X_EXPORT void SetResourceTypeErrorValue(RESTYPE /*type */ , int /*errorValue */ ); +extern _X_EXPORT SizeType GetResourceTypeSizeFunc( + RESTYPE /*type*/); + +extern _X_EXPORT void SetResourceTypeFindSubResFunc( + RESTYPE /*type*/, FindTypeSubResources /*findFunc*/); + +extern _X_EXPORT void SetResourceTypeSizeFunc( + RESTYPE /*type*/, SizeType /*sizeFunc*/); + +extern _X_EXPORT void SetResourceTypeErrorValue( + RESTYPE /*type*/, int /*errorValue*/); + extern _X_EXPORT RESTYPE CreateNewResourceClass(void); extern _X_EXPORT Bool InitClientResources(ClientPtr /*client */ ); @@ -193,6 +227,15 @@ extern _X_EXPORT void FindAllClientResources(ClientPtr /*client */ , FindAllRes /*func */ , pointer /*cdata */ ); +/** @brief Iterate through all subresources of a resource. + + @note The XID argument provided to the FindAllRes function + may be 0 for subresources that don't have an XID */ +extern _X_EXPORT void FindSubResources(pointer /*resource*/, + RESTYPE /*type*/, + FindAllRes /*func*/, + pointer /*cdata*/); + extern _X_EXPORT void FreeClientNeverRetainResources(ClientPtr /*client */ ); extern _X_EXPORT void FreeClientResources(ClientPtr /*client */ ); @@ -231,4 +274,17 @@ extern _X_EXPORT unsigned int GetXIDList(ClientPtr /*client */ , extern _X_EXPORT RESTYPE lastResourceType; extern _X_EXPORT RESTYPE TypeMask; -#endif /* RESOURCE_H */ +/** @brief A hashing function to be used for hashing resource IDs + + @param id The resource ID to hash + @param numBits The number of bits in the resulting hash. Must be >=0. + + @note This function is really only for handling + INITHASHSIZE..MAXHASHSIZE bit hashes, but will handle any number + of bits by either masking numBits lower bits of the ID or by + providing at most MAXHASHSIZE hashes. +*/ +extern _X_EXPORT int HashResourceID(XID id, + int numBits); + +#endif /* RESOURCE_H */ diff --git a/render/picture.c b/render/picture.c index 2fd13fc37..da3e49936 100644 --- a/render/picture.c +++ b/render/picture.c @@ -591,6 +591,29 @@ PictureParseCmapPolicy(const char *name) return PictureCmapPolicyInvalid; } +/** @see GetDefaultBytes */ +static void +GetPictureBytes(pointer value, XID id, ResourceSizePtr size) +{ + PicturePtr picture = value; + + /* Currently only pixmap bytes are reported to clients. */ + size->resourceSize = 0; + + size->refCnt = picture->refcnt; + + /* Calculate pixmap reference sizes. */ + size->pixmapRefSize = 0; + if (picture->pDrawable && (picture->pDrawable->type == DRAWABLE_PIXMAP)) + { + SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP); + ResourceSizeRec pixmapSize = { 0, 0, 0 }; + PixmapPtr pixmap = (PixmapPtr)picture->pDrawable; + pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize); + size->pixmapRefSize += pixmapSize.pixmapRefSize; + } +} + Bool PictureInit(ScreenPtr pScreen, PictFormatPtr formats, int nformats) { @@ -602,6 +625,7 @@ PictureInit(ScreenPtr pScreen, PictFormatPtr formats, int nformats) PictureType = CreateNewResourceType(FreePicture, "PICTURE"); if (!PictureType) return FALSE; + SetResourceTypeSizeFunc(PictureType, GetPictureBytes); PictFormatType = CreateNewResourceType(FreePictFormat, "PICTFORMAT"); if (!PictFormatType) return FALSE; diff --git a/test/Makefile.am b/test/Makefile.am index eb6147021..8c7e412c3 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -5,7 +5,7 @@ if XORG # Tests that require at least some DDX functions in order to fully link # For now, requires xf86 ddx, could be adjusted to use another SUBDIRS += xi2 -noinst_PROGRAMS += xkb input xtest misc fixes xfree86 +noinst_PROGRAMS += xkb input xtest misc fixes xfree86 hashtabletest endif check_LTLIBRARIES = libxservertest.la @@ -36,6 +36,7 @@ misc_LDADD=$(TEST_LDADD) fixes_LDADD=$(TEST_LDADD) xfree86_LDADD=$(TEST_LDADD) touch_LDADD=$(TEST_LDADD) +hashtabletest_LDADD=$(TEST_LDADD) ../Xext/hashtable.c libxservertest_la_LIBADD = $(XSERVER_LIBS) if XORG diff --git a/test/hashtabletest.c b/test/hashtabletest.c new file mode 100644 index 000000000..64c7091fc --- /dev/null +++ b/test/hashtabletest.c @@ -0,0 +1,162 @@ +#include <misc.h> +#include <stdlib.h> +#include <stdio.h> +#include "hashtable.h" +#include "resource.h" + +static void +print_xid(void* ptr, void* v) +{ + XID *x = v; + printf("%ld", *x); +} + +static void +print_int(void* ptr, void* v) +{ + int *x = v; + printf("%d", *x); +} + +static int +test1(void) +{ + HashTable h; + XID id; + int c; + int ok = 1; + const int numKeys = 420; + + printf("test1\n"); + h = ht_create(sizeof(XID), sizeof(int), ht_resourceid_hash, ht_resourceid_compare, NULL); + + for (c = 0; c < numKeys; ++c) { + int *dest; + id = c; + dest = ht_add(h, &id); + if (dest) { + *dest = 2 * c; + } + } + + printf("Distribution after insertion\n"); + ht_dump_distribution(h); + ht_dump_contents(h, print_xid, print_int, NULL); + + for (c = 0; c < numKeys; ++c) { + XID id = c; + int* v = ht_find(h, &id); + if (v) { + if (*v == 2 * c) { + // ok + } else { + printf("Key %d doesn't have expected value %d but has %d instead\n", + c, 2 * c, *v); + ok = 0; + } + } else { + ok = 0; + printf("Cannot find key %d\n", c); + } + } + + if (ok) { + printf("%d keys inserted and found\n", c); + + for (c = 0; c < numKeys; ++c) { + XID id = c; + ht_remove(h, &id); + } + + printf("Distribution after deletion\n"); + ht_dump_distribution(h); + } + + ht_destroy(h); + + return ok; +} + +static int +test2(void) +{ + HashTable h; + XID id; + int c; + int ok = 1; + const int numKeys = 420; + + printf("test2\n"); + h = ht_create(sizeof(XID), 0, ht_resourceid_hash, ht_resourceid_compare, NULL); + + for (c = 0; c < numKeys; ++c) { + id = c; + ht_add(h, &id); + } + + for (c = 0; c < numKeys; ++c) { + XID id = c; + if (!ht_find(h, &id)) { + ok = 0; + printf("Cannot find key %d\n", c); + } + } + + { + XID id = c + 1; + if (ht_find(h, &id)) { + ok = 0; + printf("Could find a key that shouldn't be there\n"); + } + } + + ht_destroy(h); + + if (ok) { + printf("Test with empty keys OK\n"); + } else { + printf("Test with empty keys FAILED\n"); + } + + return ok; +} + +static int +test3(void) +{ + int ok = 1; + HtGenericHashSetupRec hashSetup = { + .keySize = 4 + }; + HashTable h; + printf("test3\n"); + h = ht_create(4, 0, ht_generic_hash, ht_generic_compare, &hashSetup); + + if (!ht_add(h, "helo") || + !ht_add(h, "wrld")) { + printf("Could not insert keys\n"); + } + + if (!ht_find(h, "helo") || + !ht_find(h, "wrld")) { + ok = 0; + printf("Could not find inserted keys\n"); + } + + printf("Hash distribution with two strings\n"); + ht_dump_distribution(h); + + ht_destroy(h); + + return ok; +} + +int +main(void) +{ + int ok = test1(); + ok = ok && test2(); + ok = ok && test3(); + + return ok ? 0 : 1; +} |