DTS Application Library  0.2.3
Application library containing referenced objects and interfaces to common libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
refobj.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 2012 Gregory Nietsky <gregory@distrotetch.co.za>
3  http://www.distrotech.co.za
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
26 #include <pthread.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include "include/dtsapp.h"
31 
32 /* add one for ref obj's*/
34 #define REFOBJ_MAGIC 0xdeadc0de
35 
36 /* ref counted objects*/
38 struct ref_obj {
42  uint32_t magic;
45  uint32_t cnt;
48  size_t size;
50  pthread_mutex_t lock;
54  void *data;
55 };
56 
61 struct blist_obj {
64  int32_t hash;
66  struct blist_obj *next;
68  struct blist_obj *prev;
70  struct ref_obj *data;
71 };
72 
75 struct bucket_list {
77  unsigned short bucketbits;
79  size_t count;
83  struct blist_obj **list;
85  pthread_mutex_t *locks;
87  size_t *version;
88 };
89 
97 struct bucket_loop {
99  struct bucket_list *blist;
101  unsigned short bucket;
104  size_t version;
106  uint32_t head_hash;
108  uint32_t cur_hash;
110  struct blist_obj *head;
112  struct blist_obj *cur;
113 };
114 
119 #define refobj_offset sizeof(struct ref_obj);
120 
129 extern void *objalloc(int size,objdestroy destructor) {
130  struct ref_obj *ref;
131  int asize;
132  char *robj;
133 
134  asize = size + refobj_offset;
135 
136  if ((robj = malloc(asize))) {
137  memset(robj, 0, asize);
138  ref = (struct ref_obj *)robj;
139  pthread_mutex_init(&ref->lock, NULL);
140  ref->magic = REFOBJ_MAGIC;
141  ref->cnt = 1;
142  ref->data = robj + refobj_offset;
143  ref->size = asize;
144  ref->destroy = destructor;
145  return (ref->data);
146  }
147  return NULL;
148 }
149 
153 extern int objref(void *data) {
154  char *ptr = data;
155  struct ref_obj *ref;
156  int ret = 0;
157 
158  ptr = ptr - refobj_offset;
159  ref = (struct ref_obj *)ptr;
160 
161  if (!data || !ref || (ref->magic != REFOBJ_MAGIC)) {
162  return (ret);
163  }
164 
165  /*double check just incase im gone*/
166  if (!pthread_mutex_lock(&ref->lock)) {
167  if ((ref->magic == REFOBJ_MAGIC) && (ref->cnt > 0)) {
168  ref->cnt++;
169  ret = ref->cnt;
170  }
171  pthread_mutex_unlock(&ref->lock);
172  }
173 
174  return (ret);
175 }
176 
184 extern int objunref(void *data) {
185  char *ptr = data;
186  struct ref_obj *ref;
187  int ret = -1;
188 
189  if (!data) {
190  return (ret);
191  }
192 
193  ptr = ptr - refobj_offset;
194  ref = (struct ref_obj *)ptr;
195 
196  if ((ref->magic == REFOBJ_MAGIC) && (ref->cnt)) {
197  pthread_mutex_lock(&ref->lock);
198  ref->cnt--;
199  ret = ref->cnt;
200  /* free the object its no longer in use*/
201  if (!ret) {
202  ref->magic = 0;
203  ref->size = 0;
204  ref->data = NULL;
205  if (ref->destroy) {
206  ref->destroy(data);
207  }
208  pthread_mutex_unlock(&ref->lock);
209  pthread_mutex_destroy(&ref->lock);
210  free(ref);
211  } else {
212  pthread_mutex_unlock(&ref->lock);
213  }
214  }
215  return (ret);
216 }
217 
222 extern int objcnt(void *data) {
223  char *ptr = data;
224  int ret = -1;
225  struct ref_obj *ref;
226 
227  if (!data) {
228  return (ret);
229  }
230 
231  ptr = ptr - refobj_offset;
232  ref = (struct ref_obj *)ptr;
233 
234  if (ref->magic == REFOBJ_MAGIC) {
235  pthread_mutex_lock(&ref->lock);
236  ret = ref->cnt;
237  pthread_mutex_unlock(&ref->lock);
238  }
239  return (ret);
240 }
241 
246 extern int objsize(void *data) {
247  char *ptr = data;
248  int ret = 0;
249  struct ref_obj *ref;
250 
251  if (!data) {
252  return (ret);
253  }
254 
255  ptr = ptr - refobj_offset;
256  ref = (struct ref_obj *)ptr;
257 
258  if (ref->magic == REFOBJ_MAGIC) {
259  pthread_mutex_lock(&ref->lock);
260  ret = ref->size - refobj_offset;
261  pthread_mutex_unlock(&ref->lock);
262  }
263  return (ret);
264 }
265 
269 extern int objlock(void *data) {
270  char *ptr = data;
271  struct ref_obj *ref;
272 
273  ptr = ptr - refobj_offset;
274  ref = (struct ref_obj *)ptr;
275 
276  if (data && ref->magic == REFOBJ_MAGIC) {
277  pthread_mutex_lock(&ref->lock);
278  }
279  return (0);
280 }
281 
285 extern int objtrylock(void *data) {
286  char *ptr = data;
287  struct ref_obj *ref;
288 
289  ptr = ptr - refobj_offset;
290  ref = (struct ref_obj *)ptr;
291 
292  if (ref->magic == REFOBJ_MAGIC) {
293  return ((pthread_mutex_trylock(&ref->lock)) ? -1 : 0);
294  }
295  return (-1);
296 }
297 
301 extern int objunlock(void *data) {
302  char *ptr = data;
303  struct ref_obj *ref;
304 
305  ptr = ptr - refobj_offset;
306  ref = (struct ref_obj *)ptr;
307 
308  if (ref->magic == REFOBJ_MAGIC) {
309  pthread_mutex_unlock(&ref->lock);
310  }
311  return (0);
312 }
313 
314 static void empty_buckets(void *data) {
315  struct bucket_list *blist = data;
316  struct bucket_loop *bloop;
317  void *entry;
318 
319  bloop = init_bucket_loop(blist);
320  while (bloop && (entry = next_bucket_loop(bloop))) {
321  remove_bucket_loop(bloop);
322  objunref(entry);
323  }
324  objunref(bloop);
325 }
326 
330 extern void *objchar(const char *orig) {
331  int len = strlen(orig) + 1;
332  void *nobj;
333 
334  if ((nobj = objalloc(len, NULL))) {
335  memcpy(nobj, orig, len);
336  }
337  return nobj;
338 }
339 
356 extern void *create_bucketlist(int bitmask, blisthash hash_function) {
357  struct bucket_list *new;
358  short int buckets, cnt;
359 
360  buckets = (1 << bitmask);
361 
362  /* allocate session bucket list memory size of the struct plus a list lock and version for each bucket*/
363  if (!(new = objalloc(sizeof(*new) + (sizeof(void *) + sizeof(pthread_mutex_t) + sizeof(size_t)) * buckets, empty_buckets))) {
364  return NULL;
365  }
366 
367  /*initialise each bucket*/
368  new->bucketbits = bitmask;
369  new->list = (void *)((char *)new + sizeof(*new));
370  for (cnt = 0; cnt < buckets; cnt++) {
371  if ((new->list[cnt] = malloc(sizeof(*new->list[cnt])))) {
372  memset(new->list[cnt], 0, sizeof(*new->list[cnt]));
373  }
374  }
375 
376  /*next pointer is pointer to locks*/
377  new->locks = (void *)&new->list[buckets];
378  for (cnt = 0; cnt < buckets; cnt++) {
379  pthread_mutex_init(&new->locks[cnt], NULL);
380  }
381 
382  /*Next up version array*/
383  new->version = (void *)&new->locks[buckets];
384 
385  new->hash_func = hash_function;
386 
387  return (new);
388 }
389 
390 static struct blist_obj *blist_gotohash(struct blist_obj *cur, unsigned int hash, int bucketbits) {
391  struct blist_obj *lhead = cur;
392 
393  if ((hash << bucketbits) < 0) {
394  do {
395  lhead = lhead->prev;
396  } while ((lhead->hash > hash) && lhead->prev->next);
397  } else {
398  while (lhead && lhead->next && (lhead->next->hash < hash)) {
399  lhead = lhead->next;
400  }
401  }
402 
403  return (lhead);
404 }
405 
406 static int gethash(struct bucket_list *blist, const void *data, int key) {
407  const char *ptr = data;
408  struct ref_obj *ref;
409  int hash = 0;
410 
411  ptr = ptr - refobj_offset;
412  ref = (struct ref_obj *)ptr;
413 
414  if (blist->hash_func) {
415  hash = blist->hash_func(data, key);
416  } else if (ref && (ref->magic == REFOBJ_MAGIC)) {
417  hash = jenhash(ref, ref->size, 0);
418  }
419  return (hash);
420 }
421 
428 extern int addtobucket(struct bucket_list *blist, void *data) {
429  char *ptr = data;
430  struct ref_obj *ref;
431  struct blist_obj *lhead, *tmp;
432  unsigned int hash, bucket;
433 
434  if (!objref(blist)) {
435  return (0);
436  }
437 
438  if (!objref(data)) {
439  objunref(blist);
440  return (0);
441  }
442 
443  ptr = ptr - refobj_offset;
444  ref = (struct ref_obj *)ptr;
445 
446  hash = gethash(blist, data, 0);
447  bucket = ((hash >> (32 - blist->bucketbits)) & ((1 << blist->bucketbits) - 1));
448 
449  pthread_mutex_lock(&blist->locks[bucket]);
450  lhead = blist->list[bucket];
451  /*no head or non null head*/
452  if (!lhead || lhead->prev) {
453  if (!(tmp = malloc(sizeof(*tmp)))) {
454  pthread_mutex_unlock(&blist->locks[bucket]);
455  objunref(data);
456  objunref(blist);
457  return (0);
458  }
459  memset(tmp, 0, sizeof(*tmp));
460  tmp->hash = hash;
461  tmp->data = ref;
462 
463  /*there is no head*/
464  if (!lhead) {
465  blist->list[bucket] = tmp;
466  tmp->prev = tmp;
467  tmp->next = NULL;
468  /*become new head*/
469  } else if (hash < lhead->hash) {
470  tmp->next = lhead;
471  tmp->prev = lhead->prev;
472  lhead->prev = tmp;
473  blist->list[bucket] = tmp;
474  /*new tail*/
475  } else if (hash > lhead->prev->hash) {
476  tmp->prev = lhead->prev;
477  tmp->next = NULL;
478  lhead->prev->next = tmp;
479  lhead->prev = tmp;
480  /*insert entry*/
481  } else {
482  lhead = blist_gotohash(lhead, hash, blist->bucketbits);
483  tmp->next = lhead->next;
484  tmp->prev = lhead;
485 
486  if (lhead->next) {
487  lhead->next->prev = tmp;
488  } else {
489  blist->list[bucket]->prev = tmp;
490  }
491  lhead->next = tmp;
492  }
493  } else {
494  /*set NULL head*/
495  lhead->data = ref;
496  lhead->prev = lhead;
497  lhead->next = NULL;
498  lhead->hash = hash;
499  }
500 
501  blist->version[bucket]++;
502  pthread_mutex_unlock(&blist->locks[bucket]);
503 
504  objlock(blist);
505  blist->count++;
506  objunlock(blist);
507  objunref(blist);
508 
509  return (1);
510 }
511 
517 extern void remove_bucket_item(struct bucket_list *blist, void *data) {
518  struct blist_obj *entry;
519  int hash, bucket;
520 
521  hash = gethash(blist, data, 0);
522  bucket = ((hash >> (32 - blist->bucketbits)) & ((1 << blist->bucketbits) - 1));
523 
524  pthread_mutex_lock(&blist->locks[bucket]);
525  entry = blist_gotohash(blist->list[bucket], hash + 1, blist->bucketbits);
526  if (entry && entry->hash == hash) {
527  if (entry->next && (entry == blist->list[bucket])) {
528  entry->next->prev = entry->prev;
529  blist->list[bucket] = entry->next;
530  } else if (entry->next) {
531  entry->next->prev = entry->prev;
532  entry->prev->next = entry->next;
533  } else if (entry == blist->list[bucket]) {
534  blist->list[bucket] = NULL;
535  } else {
536  entry->prev->next = NULL;
537  blist->list[bucket]->prev = entry->prev;
538  }
539  objunref(entry->data->data);
540  free(entry);
541  objlock(blist);
542  blist->count--;
543  blist->version[bucket]++;
544  objunlock(blist);
545  }
546  pthread_mutex_unlock(&blist->locks[bucket]);
547 }
548 
552 extern int bucket_list_cnt(struct bucket_list *blist) {
553  int ret = -1;
554 
555  if (blist) {
556  objlock(blist);
557  ret = blist->count;
558  objunlock(blist);
559  }
560  return (ret);
561 }
562 
572 extern void *bucket_list_find_key(struct bucket_list *blist, const void *key) {
573  struct blist_obj *entry;
574  int hash, bucket;
575 
576  if (!blist) {
577  return (NULL);
578  }
579 
580  hash = gethash(blist, key, 1);
581  bucket = ((hash >> (32 - blist->bucketbits)) & ((1 << blist->bucketbits) - 1));
582 
583  pthread_mutex_lock(&blist->locks[bucket]);
584  entry = blist_gotohash(blist->list[bucket], hash + 1, blist->bucketbits);
585  if (entry && entry->data) {
586  objref(entry->data->data);
587  } else
588  if (!entry) {
589  pthread_mutex_unlock(&blist->locks[bucket]);
590  return NULL;
591  }
592 
593  pthread_mutex_unlock(&blist->locks[bucket]);
594 
595  if (entry->data && (entry->hash == hash)) {
596  return (entry->data->data);
597  } else
598  if (entry->data) {
599  objunref(entry->data->data);
600  }
601 
602  return NULL;
603 }
604 
613 extern void bucketlist_callback(struct bucket_list *blist, blist_cb callback, void *data2) {
614  struct bucket_loop *bloop;
615  void *data;
616 
617  if (!blist || !callback) {
618  return;
619  }
620 
621  bloop = init_bucket_loop(blist);
622  while(blist && bloop && (data = next_bucket_loop(bloop))) {
623  callback(data, data2);
624  objunref(data);
625  }
626  objunref(bloop);
627 }
628 
629 static void free_bloop(void *data) {
630  struct bucket_loop *bloop = data;
631 
632  if (bloop->blist) {
633  objunref(bloop->blist);
634  }
635 }
636 
640 extern struct bucket_loop *init_bucket_loop(struct bucket_list *blist) {
641  struct bucket_loop *bloop = NULL;
642 
643  if (blist && (bloop = objalloc(sizeof(*bloop), free_bloop))) {
644  objref(blist);
645  bloop->blist = blist;
646  bloop->bucket = 0;
647  pthread_mutex_lock(&blist->locks[bloop->bucket]);
648  bloop->head = blist->list[0];
649  if (bloop->head) {
650  bloop->head_hash = bloop->head->hash;
651  };
652  bloop->version = blist->version[0];
653  pthread_mutex_unlock(&blist->locks[bloop->bucket]);
654  }
655 
656  return (bloop);
657 }
658 
662 extern void *next_bucket_loop(struct bucket_loop *bloop) {
663  struct bucket_list *blist = bloop->blist;
664  struct ref_obj *entry = NULL;
665  void *data = NULL;
666 
667  pthread_mutex_lock(&blist->locks[bloop->bucket]);
668  if (bloop->head_hash && (blist->version[bloop->bucket] != bloop->version)) {
669  /* bucket has changed unexpectedly i need to ff/rew to hash*/
670  bloop->head = blist_gotohash(blist->list[bloop->bucket], bloop->head_hash + 1, blist->bucketbits);
671  /*if head has gone find next suitable ignore any added*/
672  while (bloop->head && (bloop->head->hash < bloop->head_hash)) {
673  bloop->head = bloop->head->next;
674  }
675  }
676 
677  while (!bloop->head || !bloop->head->prev) {
678  pthread_mutex_unlock(&blist->locks[bloop->bucket]);
679  bloop->bucket++;
680  if (bloop->bucket < (1 << blist->bucketbits)) {
681  pthread_mutex_lock(&blist->locks[bloop->bucket]);
682  bloop->head = blist->list[bloop->bucket];
683  } else {
684  return NULL;
685  }
686  }
687 
688  if (bloop->head) {
689  bloop->cur = bloop->head;
690  entry = (bloop->head->data) ? bloop->head->data : NULL;
691  data = (entry) ? entry->data : NULL;
692  objref(data);
693  bloop->head = bloop->head->next;
694  bloop->head_hash = (bloop->head) ? bloop->head->hash : 0;
695  bloop->cur_hash = (bloop->cur) ? bloop->cur->hash : 0;
696  }
697  pthread_mutex_unlock(&blist->locks[bloop->bucket]);
698 
699  return (data);
700 }
701 
702 
710 extern void remove_bucket_loop(struct bucket_loop *bloop) {
711  struct bucket_list *blist = bloop->blist;
712  int bucket = bloop->bucket;
713 
714  pthread_mutex_lock(&blist->locks[bloop->bucket]);
715  /*if the bucket has altered need to verify i can remove*/
716  if (bloop->cur_hash && (!bloop->cur || (blist->version[bloop->bucket] != bloop->version))) {
717  bloop->cur = blist_gotohash(blist->list[bloop->bucket], bloop->cur_hash + 1, blist->bucketbits);
718  if (!bloop->cur || (bloop->cur->hash != bloop->cur_hash)) {
719  pthread_mutex_unlock(&blist->locks[bucket]);
720  return;
721  }
722  }
723 
724  if (!bloop->cur) {
725  pthread_mutex_unlock(&blist->locks[bucket]);
726  return;
727  }
728 
729  if (bloop->cur->next && (bloop->cur == blist->list[bucket])) {
730  bloop->cur->next->prev = bloop->cur->prev;
731  blist->list[bucket] = bloop->cur->next;
732  } else if (bloop->cur->next) {
733  bloop->cur->next->prev = bloop->cur->prev;
734  bloop->cur->prev->next = bloop->cur->next;
735  } else if (bloop->cur == blist->list[bucket]) {
736  blist->list[bucket] = NULL;
737  } else {
738  bloop->cur->prev->next = NULL;
739  blist->list[bucket]->prev = bloop->cur->prev;
740  }
741 
742  objunref(bloop->cur->data->data);
743  free(bloop->cur);
744  bloop->cur_hash = 0;
745  bloop->cur = NULL;
746  blist->version[bucket]++;
747  bloop->version++;
748  pthread_mutex_unlock(&blist->locks[bucket]);
749 
750  objlock(blist);
751  blist->count--;
752  objunlock(blist);
753 }
754 
Bucket iterator.
Definition: refobj.c:97
#define refobj_offset
The size of ref_obj is the offset for the data.
Definition: refobj.c:119
void * create_bucketlist(int bitmask, blisthash hash_function)
Definition: refobj.c:356
struct blist_obj ** list
Array of blist_obj one per bucket ie 2^bucketbits.
Definition: refobj.c:83
int objref(void *data)
Reference a object.
Definition: refobj.c:153
blisthash hash_func
Hash function called to calculate the hash and thus the bucket its placed in.
Definition: refobj.c:81
uint32_t head_hash
Hash of head if we need to comeback.
Definition: refobj.c:106
unsigned short bucket
Active bucket as determined by hash.
Definition: refobj.c:101
int objlock(void *data)
Lock the reference.
Definition: refobj.c:269
void * objchar(const char *orig)
Return a reference to copy of a buffer.
Definition: refobj.c:330
int32_t hash
Hash value calculated from the data.
Definition: refobj.c:64
Entry in a bucket list.
Definition: refobj.c:61
void * objalloc(int size, objdestroy)
Allocate a referenced lockable object.
Definition: refobj.c:129
struct blist_obj * head
Current bucket.
Definition: refobj.c:110
int bucket_list_cnt(struct bucket_list *blist)
Return number of items in the list.
Definition: refobj.c:552
int objcnt(void *data)
Return current reference count.
Definition: refobj.c:222
void * next_bucket_loop(struct bucket_loop *bloop)
Return a reference to the next item in the list this could be the first item.
Definition: refobj.c:662
size_t count
Number of items held.
Definition: refobj.c:79
struct blist_obj * cur
Current item.
Definition: refobj.c:112
void(* objdestroy)(void *)
Callback used to clean data of a reference object when it is to be freed.
Definition: dtsapp.h:264
objdestroy destroy
Function to call to clean up the data before its freed.
Definition: refobj.c:52
DTS Application library API Include file.
size_t * version
version of the bucket to detect changes during iteration (loop)
Definition: refobj.c:87
int objtrylock(void *data)
Try lock a reference.
Definition: refobj.c:285
Internal structure of all referenced objects.
Definition: refobj.c:38
struct blist_obj * prev
Previous entry in the bucket.
Definition: refobj.c:68
void * data
Pointer to the data referenced.
Definition: refobj.c:54
size_t version
Our version check this with blist to determine if we must rewined and fast forward.
Definition: refobj.c:104
#define REFOBJ_MAGIC
Magic number stored as first field of all referenced objects.
Definition: refobj.c:34
size_t size
The size allocated to this object.
Definition: refobj.c:48
int objunlock(void *data)
Unlock a reference.
Definition: refobj.c:301
uint32_t magic
Memory integrity check used to prevent non refeferenced objects been handled as referenced objects...
Definition: refobj.c:42
struct blist_obj * next
Next entry in the bucket.
Definition: refobj.c:66
void(* blist_cb)(void *, void *)
This callback is run on each entry in a list.
Definition: dtsapp.h:278
uint32_t cnt
Reference count the oject will be freed when the reference count reaches 0.
Definition: refobj.c:45
void bucketlist_callback(struct bucket_list *blist, blist_cb callback, void *data2)
Run a callback function on all items in the list.
Definition: refobj.c:613
#define jenhash(key, length, initval)
Define jenhash as hashlittle on big endian it should be hashbig.
Definition: dtsapp.h:914
struct bucket_list * blist
Referenece to the bucket been itereated.
Definition: refobj.c:99
void remove_bucket_loop(struct bucket_loop *bloop)
Safely remove a item from a list while iterating in a loop.
Definition: refobj.c:710
pthread_mutex_t * locks
Array of locks one per bucket.
Definition: refobj.c:85
unsigned short bucketbits
number of buckets 2^n
Definition: refobj.c:77
int objsize(void *data)
Size requested for data.
Definition: refobj.c:246
uint32_t cur_hash
Hash of cur if we need to comeback.
Definition: refobj.c:108
int addtobucket(struct bucket_list *blist, void *data)
Add a reference to the bucketlist.
Definition: refobj.c:428
void * bucket_list_find_key(struct bucket_list *list, const void *key)
Find and return a reference to a item matching supplied key.
Definition: refobj.c:572
void remove_bucket_item(struct bucket_list *blist, void *data)
Remove and unreference a item from the list.
Definition: refobj.c:517
int objunref(void *data)
Drop reference held.
Definition: refobj.c:184
struct ref_obj * data
Reference to data held.
Definition: refobj.c:70
int32_t(* blisthash)(const void *, int)
Callback used to calculate the hash of a structure.
Definition: dtsapp.h:271
struct bucket_loop * init_bucket_loop(struct bucket_list *blist)
Create a bucket list iterator to safely iterate the list.
Definition: refobj.c:640
pthread_mutex_t lock
this is a pointer to the lock it may be changed to be the lock
Definition: refobj.c:50
Bucket list, hold hashed objects in buckets.
Definition: refobj.c:75