Browse code

started doxygen documentation of CDS library and little changes which seemed useful when creating documentation (reference counters, removed library init and cleanup function because it is not needed more)

Vaclav Kubart authored on 10/05/2007 11:23:49
Showing 7 changed files
... ...
@@ -3,6 +3,8 @@
3 3
 #include <cds/sync.h>
4 4
 #include <cds/logger.h>
5 5
 
6
+#if 0
7
+
6 8
 typedef struct {
7 9
 	int init_cnt;
8 10
 } init_data_t;
... ...
@@ -55,3 +57,4 @@ void cds_cleanup()
55 55
 	}
56 56
 }
57 57
 
58
+#endif
... ...
@@ -1,17 +1,77 @@
1 1
 #ifndef __CDS_H
2 2
 #define __CDS_H
3 3
 
4
-/* declaration of initialization/destruction functions */
4
+/** \defgroup cds CDS library - Common Data Structures 
5
+ *
6
+ * This library contains many useful functions and data structures. It is
7
+ possible to use it with Sip Express Router (SER) or without it. In the first
8
+ case it is able to use some internal SER's data types like strings.
9
+ *
10
+ * \section cds_conventions Conventions
11
+ *  - data types (structures, enums, ...) have their names with suffix \c _t
12
+ *  (\c str_t, \c dstring_t, ...)
13
+ *  - many functions have prefix according to data structure on which are
14
+ *  operating (like dstr_append which operates on dstring_t data structure)
15
+ *  - most functions return 0 as successful result and nonzero value as error
16
+ *
17
+ * \section cds_dependencies Dependencies
18
+ * This library depends only on standard C libraries and needs no other
19
+ * external libraries.
20
+ *
21
+ * \section cds_ser_usage Usage with SER
22
+ * There can be problems with using shared libraries on different platforms.
23
+ * Currently supported solution is that user must supply LD_LIBRARY_PATH
24
+ * (or something similar on his OS) with the path to the library before 
25
+ * starting SER with modules needed the library.
26
+ *
27
+ * \section cds_nonser_usage Usage without SER
28
+ * The library can be compiled without dependencies on SER and can be linked
29
+ * to whatever program as any other dynamic library. This style of usage is
30
+ * probably used only when debugging problems which is hard to find with SER
31
+ * (outside of SER is possible to use tools like Valgrind) and for author's
32
+ * little programs and need not to be documented more... ;-)
33
+ *
34
+ * \par History
35
+ * There were following reasons to introduce this library:
36
+ *  - many duplicated functions in modules (copy&pasted between modules)
37
+ *  without touching SER's core
38
+ *  - possibility to debug programs outside of SER due to its simplicity 
39
+ *  and many usefull tools
40
+ *
41
+ * @{ */
5 42
 
6 43
 #ifdef __cplusplus
7 44
 extern "C" {
8 45
 #endif
9
-	
46
+
47
+#if 0
48
+/* not used */
49
+
50
+/** \defgroup cds_init Initialization/destruction
51
+ * Library needs to be initialized before using it and
52
+ * un-initialized after it is not used more. Use \ref cds_initialize and 
53
+ * \ref cds_cleanup for this purpose.
54
+ * @{ */
55
+
56
+/** Initializes CDS library. 
57
+ *
58
+ * Currently initializes reference counter which is experimental and 
59
+ * probably will be removed in the future because seems to be rather 
60
+ * useless here. */
10 61
 int cds_initialize();
62
+
63
+/** Cleans up after CDS. After calling this function can't be called
64
+ * reference counter functions. */
11 65
 void cds_cleanup();
12 66
 
67
+#endif
68
+
69
+/** @} */
70
+
13 71
 #ifdef __cplusplus
14 72
 }
15 73
 #endif
16 74
 
75
+/** @} */
76
+
17 77
 #endif
... ...
@@ -32,6 +32,17 @@
32 32
 extern "C" {
33 33
 #endif
34 34
 
35
+/** \ingroup cds
36
+ * @{ 
37
+ *
38
+ * \defgroup cds_memory Memory management
39
+ *
40
+ * Memory operations are common for whole CDS library. Because it must work
41
+ together with SER's memory management and must work without it too, there are
42
+ wrapper macros for memory allocation/deallocation.
43
+ * 
44
+ * @{ */
45
+
35 46
 /* typedef void*(*cds_malloc_func)(unsigned int size);
36 47
 typedef void(*cds_free_func)(void *ptr);
37 48
 
... ...
@@ -40,16 +51,64 @@ extern cds_free_func cds_free;
40 40
 
41 41
 void cds_set_memory_functions(cds_malloc_func _malloc, cds_free_func _free); */
42 42
 
43
+/** \def cds_malloc(s)
44
+ * Function/macro for memory allocation. Which function is choosen depends on
45
+ * SER and TRACE_CDS_MEMORY defines. 
46
+ *
47
+ * When SER is defined shm_malloc is choosen, standard malloc otherwise. */
48
+
49
+/** \def cds_free(p)
50
+ * Function/macro for memory deallocation. Which function is choosen depends
51
+ * on SER and TRACE_CDS_MEMORY defines. 
52
+ *
53
+ * If SER is defined shm_free is choosen, standard free otherwise. */
54
+
55
+/** \def cds_malloc_ptr
56
+ * Function/macro for memory allocation when pointer to function needed. Which
57
+ * function is choosen depends on SER and TRACE_CDS_MEMORY defines. 
58
+ *
59
+ * If SER is defined shm_malloc is choosen, standard malloc otherwise.  */
60
+
61
+/** \def cds_free_ptr
62
+ * Function/macro for memory deallocation when pointer to function needed.
63
+ * Which function is choosen depends on SER and TRACE_CDS_MEMORY defines. 
64
+ *
65
+ * If SER is defined shm_free is choosen, standard free otherwise.  */
66
+
67
+/** \def cds_malloc_pkg(s)
68
+ * Function/macro for 'local' memory allocation. Which function is choosen
69
+ * depends on SER and TRACE_CDS_MEMORY defines. 
70
+ *
71
+ * When SER is defined pkg_malloc is choosen, standard malloc otherwise. */
72
+
73
+/** \def cds_free_pkg(p)
74
+ * Function/macro for 'local' memory deallocation. Which function is choosen
75
+ * depends on SER and TRACE_CDS_MEMORY defines. 
76
+ *
77
+ * When SER is defined pkg_free is choosen, standard free otherwise. */
78
+
43 79
 #ifdef TRACE_CDS_MEMORY
44 80
 
81
+/** \internal Debugging variant of alloc function */
45 82
 void *debug_malloc(int size, const char *file, int line);
83
+
84
+/** \internal Debugging variant of free function */
46 85
 void debug_free(void *block, const char *file, int line);
86
+
87
+/** \internal Another debugging variant of alloc function - used when pointer
88
+ * to function needed. */
47 89
 void *debug_malloc_ex(unsigned int size);
90
+
91
+/** \internal Another debugging variant of free function - used when pointer to
92
+ * function needed. */
48 93
 void debug_free_ex(void *block);
49 94
 
50
-/* trace function */
95
+/* \internal Helper function for debugging - shows some debugging information about
96
+ * memory allocations (currently only the number of allocated blocks). */
51 97
 void cds_memory_trace(char *dst, int dst_len);
52
-/* initializes internal variables for memory tracing */
98
+
99
+/** \internal Helper function which is useful for memory debugging only - initializes
100
+ * internal variables for memory tracing */
53 101
 void cds_memory_trace_init();
54 102
 
55 103
 #define cds_malloc(s)	debug_malloc(s,__FILE__, __LINE__)
... ...
@@ -95,6 +154,8 @@ void shm_free_x(void *ptr);
95 95
 }
96 96
 #endif
97 97
 
98
+/** @} 
99
+ * @} */
98 100
 
99 101
 #endif
100 102
 
... ...
@@ -93,14 +93,14 @@ int push_message(msg_queue_t *q, mq_message_t *m)
93 93
 	if ((!q) || (!m)) return -1;
94 94
 	m->next = NULL;
95 95
 	
96
-	if (q->use_mutex) cds_mutex_lock(&q->q_mutex);
96
+	if (q->flags & MQ_USE_MUTEX) cds_mutex_lock(&q->q_mutex);
97 97
 	if (q->last) q->last->next = m;
98 98
 	else {
99 99
 		q->first = m;
100 100
 		q->last = m;
101 101
 	}
102 102
 	q->last = m;
103
-	if (q->use_mutex) cds_mutex_unlock(&q->q_mutex);
103
+	if (q->flags & MQ_USE_MUTEX) cds_mutex_unlock(&q->q_mutex);
104 104
 	
105 105
 	return 0;
106 106
 }
... ...
@@ -110,11 +110,11 @@ int mq_add_to_top(msg_queue_t *q, mq_message_t *m)
110 110
 	if ((!q) || (!m)) return -1;
111 111
 	m->next = NULL;
112 112
 	
113
-	if (q->use_mutex) cds_mutex_lock(&q->q_mutex);
113
+	if (q->flags & MQ_USE_MUTEX) cds_mutex_lock(&q->q_mutex);
114 114
 	m->next = q->first;
115 115
 	q->first = m;
116 116
 	if (!q->last) q->last = m;
117
-	if (q->use_mutex) cds_mutex_unlock(&q->q_mutex);
117
+	if (q->flags & MQ_USE_MUTEX) cds_mutex_unlock(&q->q_mutex);
118 118
 	
119 119
 	return 0;
120 120
 }
... ...
@@ -124,7 +124,7 @@ mq_message_t *pop_message(msg_queue_t *q)
124 124
 	mq_message_t *m;
125 125
 	if (!q) return NULL;
126 126
 
127
-	if (q->use_mutex) cds_mutex_lock(&q->q_mutex);
127
+	if (q->flags & MQ_USE_MUTEX) cds_mutex_lock(&q->q_mutex);
128 128
 	m = q->first;
129 129
 	if (m) {
130 130
 		if (q->first == q->last) {
... ...
@@ -134,18 +134,17 @@ mq_message_t *pop_message(msg_queue_t *q)
134 134
 		else q->first = m->next;
135 135
 		m->next = NULL;
136 136
 	}
137
-	if (q->use_mutex) cds_mutex_unlock(&q->q_mutex);
137
+	if (q->flags & MQ_USE_MUTEX) cds_mutex_unlock(&q->q_mutex);
138 138
 		
139 139
 	return m;
140 140
 }
141 141
 
142
-/** 1 ... empty, 0 ...  NOT empty !! */
143 142
 int is_msg_queue_empty(msg_queue_t *q)
144 143
 {
145 144
 	int res = 1;
146
-	if (q->use_mutex) cds_mutex_lock(&q->q_mutex);
145
+	if (q->flags & MQ_USE_MUTEX) cds_mutex_lock(&q->q_mutex);
147 146
 	if (q->first) res = 0;
148
-	if (q->use_mutex) cds_mutex_unlock(&q->q_mutex);
147
+	if (q->flags & MQ_USE_MUTEX) cds_mutex_unlock(&q->q_mutex);
149 148
 	return res;
150 149
 }
151 150
 
... ...
@@ -156,20 +155,31 @@ int msg_queue_init(msg_queue_t *q)
156 156
 
157 157
 int msg_queue_init_ex(msg_queue_t *q, int synchronize)
158 158
 {
159
-	if (synchronize) cds_mutex_init(&q->q_mutex);
160
-	init_reference_counter(&q->ref);
161
-	q->use_mutex = synchronize;
159
+	if (synchronize) {
160
+		cds_mutex_init(&q->q_mutex);
161
+		q->flags = MQ_USE_MUTEX;
162
+	}
163
+	else q->flags = 0;
162 164
 	q->first = NULL;
163 165
 	q->last = NULL;
164 166
 	return 0;
165 167
 }
166 168
 
167
-void msg_queue_destroy(msg_queue_t *q)
169
+/** \internal Destroys all internal data of message queue and 
170
+ * optionaly frees it if no more references exist. */
171
+static inline void msg_queue_destroy_and_free(msg_queue_t *q, int do_free)
168 172
 {
169 173
 	mq_message_t *m,*n;
170 174
 	if (!q) return;
171 175
 	
172
-	if (q->use_mutex) cds_mutex_lock(&q->q_mutex);
176
+	if (q->flags & MQ_USE_REF_CNTR) {
177
+		if (!remove_reference(&q->ref)) {
178
+			/* this was NOT the last reference */
179
+			return;
180
+		}
181
+	}
182
+
183
+	if (q->flags & MQ_USE_MUTEX) cds_mutex_lock(&q->q_mutex);
173 184
 	m = q->first;
174 185
 	while (m) {
175 186
 		n = m->next;
... ...
@@ -178,17 +188,29 @@ void msg_queue_destroy(msg_queue_t *q)
178 178
 	}
179 179
 	q->first = NULL;
180 180
 	q->last = NULL;
181
-	if (q->use_mutex) {
181
+	if (q->flags & MQ_USE_MUTEX) {
182 182
 		cds_mutex_unlock(&q->q_mutex);
183 183
 		cds_mutex_destroy(&q->q_mutex);
184 184
 	}
185
+	if (do_free) cds_free(q);
186
+}
187
+
188
+void msg_queue_destroy(msg_queue_t *q)
189
+{
190
+	msg_queue_destroy_and_free(q, 0);
185 191
 }
186 192
 
187 193
 void msg_queue_free(msg_queue_t *q)
188 194
 {
189
-	if (remove_reference(&q->ref)) {
190
-		msg_queue_destroy(q);
191
-		cds_free(q);
195
+	msg_queue_destroy_and_free(q, 1);
196
+}
197
+
198
+void msg_queue_init_ref_cnt(msg_queue_t *q, reference_counter_group_t *grp)
199
+{
200
+	if (grp) {
201
+		init_reference_counter(grp, &q->ref);
202
+		q->flags |= MQ_USE_REF_CNTR;
192 203
 	}
204
+	else q->flags &= ~MQ_USE_REF_CNTR; /* don't use reference counter */
193 205
 }
194 206
 
... ...
@@ -33,8 +33,31 @@
33 33
 extern "C" {
34 34
 #endif
35 35
 
36
+/** \ingroup cds
37
+ * @{ 
38
+ *
39
+ * \defgroup cds_msg_queue Message Queue
40
+ *
41
+ * Message queue is a structure useful for sending data between processes.
42
+ * It can be synchronized via its own mutex or the synchronization can
43
+ * be left on caller. It can use reference counter which is useful
44
+ * when accessing dynamicaly allocated queue destroyed by its last user.
45
+ *
46
+ * @{
47
+ * */
48
+
36 49
 typedef void (*destroy_function_f)(void *);
37 50
 
51
+/** Structure holding message which can be put
52
+ * into message queue.
53
+ *
54
+ * There is a need to allow destroing the message without knowing its
55
+ * internals (destroying message queue with non-processed messages) and thus
56
+ * the destroy_function able to fully destroy whole data hold by message must
57
+ * be given. It is mostly needed to choose the function manually only for
58
+ * complex data with pointers which content need to be freed too.
59
+ * For simple structures it is set automaticaly during the message creation.
60
+ */
38 61
 typedef struct _mq_message_t {
39 62
 	void *data;
40 63
 	int data_len;
... ...
@@ -47,50 +70,90 @@ typedef struct _mq_message_t {
47 47
 	char data_buf[1];
48 48
 } mq_message_t;
49 49
 
50
+
51
+/** Message queue flag meaning that internal queue mutex is used for
52
+ * synchronization. It is set during message queue initialization via \ref
53
+ * msg_queue_init or \ref msg_queue_init_ex. */
54
+#define MQ_USE_MUTEX	1
55
+
56
+/** Message queue flag meaning that reference counters are used. 
57
+ * To set this flag is needed to call \ref msg_queue_init_ref_cnt with
58
+ * non-NULL group parameter. */ 
59
+#define MQ_USE_REF_CNTR	2
60
+
50 61
 typedef struct msg_queue {
51 62
 	reference_counter_data_t ref;
52 63
 	mq_message_t *first;
53 64
 	mq_message_t *last;
54 65
 	cds_mutex_t q_mutex;
55
-	int use_mutex;
66
+	unsigned int flags;
56 67
 } msg_queue_t;
57 68
 
58 69
 #define get_message_data(msg)		(msg ? msg->data: NULL)
59 70
 #define get_message_data_len(msg)	(msg ? msg->data_len: 0)
60 71
 
61
-/** the space for data is allocated in messages data
72
+/** The space for data is allocated in messages data
62 73
  * (they are automaticaly freed!)! Pointer to allocated
63 74
  * data bytes is in data variable in the message structure. */
64 75
 mq_message_t *create_message_ex(int data_len);
65 76
 
66
-/** data must be allocated using cds_malloc and they
67
- * are automacicaly freed! */
77
+/** Creates message holding data allocated using cds_malloc.
78
+ * Warning: data must be allocated using cds_malloc and they
79
+ * are automacicaly freed by free_message! */
68 80
 mq_message_t *create_message(void *data, int data_len);
69 81
 
82
+/** Sets function which will be called by free_message to destroy data. 
83
+ *
84
+ * This function may be useful when a complex structure with pointers is added
85
+ * as data parameter.  */
70 86
 void set_data_destroy_function(mq_message_t *msg, destroy_function_f func);
71 87
 
72
-/** initializes message, 
73
- * if auto_free set, data must be allocated using cds_malloc and are automaticaly freed by free_message 
74
- * (and if msg_queue_destroy called) */
88
+/** Initializes message.
89
+ * If auto_free set, data must be allocated using cds_malloc and are
90
+ * automaticaly freed by free_message (and if msg_queue_destroy called) */
75 91
 void init_message_ex(mq_message_t *m, void *data, int data_len, destroy_function_f func);
76 92
 
77
-/** frees the message and data holding by the message !!!! */
93
+/** Frees the message and data hold by the message. */
78 94
 void free_message(mq_message_t *msg);
79 95
 
96
+/** Put message into queue. */
80 97
 int push_message(msg_queue_t *q, mq_message_t *m);
98
+
99
+/** Remove message from queue. */
81 100
 mq_message_t *pop_message(msg_queue_t *q);
82 101
 
83
-/** 1 ... empty, 0 ...  NOT empty !! */
102
+/** Tests if message queue holds a message.
103
+ * \retval 1 if empty
104
+ * \retval 0 if NOT empty. */
84 105
 int is_msg_queue_empty(msg_queue_t *q);
85 106
 
86
-/** initializes synchronized message queue */
107
+/** Initializes message queue with a mutex guarding queue operations. */
87 108
 int msg_queue_init(msg_queue_t *q);
109
+
110
+/** Initializes message queue. If synchronize is set it initializes
111
+ * a mutex guarding queue operations otherwise the message queue remains
112
+ * unsynchronized. */
88 113
 int msg_queue_init_ex(msg_queue_t *q, int synchronize);
114
+
115
+/** Initializes reference counter for given message queue
116
+ * \param grp specifies group of reference counters to use. The message
117
+ * queue will stop using the reference counter if NULL. 
118
+ * \param q specifies the message queue */
119
+void msg_queue_init_ref_cnt(msg_queue_t *q, reference_counter_group_t *grp);
120
+
121
+/** Destroys message queue if no more references exist. 
122
+ * This function destroys all message queue internal data but doesn't free
123
+ * the message queue itself. It can be useful for staticaly allocated queues
124
+ * or when allocated not using cds_malloc. */
89 125
 void msg_queue_destroy(msg_queue_t *q);
90 126
 
91
-/* removes reference to message queue and frees it if no other references exist */
127
+/** Destroys and frees the message queue if no more references exist.
128
+ * It uses cds_free for freeing the memory. */
92 129
 void msg_queue_free(msg_queue_t *q);
93 130
 
131
+/** @} 
132
+ @} */
133
+
94 134
 #ifdef __cplusplus
95 135
 }
96 136
 #endif
... ...
@@ -2,42 +2,50 @@
2 2
 #include <cds/logger.h>
3 3
 #include <cds/memory.h>
4 4
 
5
-/* One global mutex for reference counting may be enough. 
6
- * If problems try to create pool of precreated mutexes
7
- * and use them randomly.
8
- */
9
-static cds_mutex_t *ref_cntr_mutex = NULL;
5
+/* functions for initialization and destruction */
10 6
 
11
-/* global functions for initialization and destruction */
12
-
13
-int reference_counter_initialize()
7
+reference_counter_group_t *create_reference_counter_group(int mutex_cnt)
14 8
 {
15
-	if (!ref_cntr_mutex) {
16
-		ref_cntr_mutex = (cds_mutex_t*)cds_malloc(sizeof(cds_mutex_t));
17
-		if (ref_cntr_mutex) {
18
-			cds_mutex_init(ref_cntr_mutex);
19
-			return 0;
20
-		}
9
+	reference_counter_group_t *g;
10
+	int i;
11
+
12
+	g = cds_malloc(sizeof(*g) + mutex_cnt * sizeof(cds_mutex_t));
13
+	if (!g) {
14
+		ERROR_LOG("can't allocate memory\n");
15
+		return NULL;
16
+	}
17
+
18
+	for (i = 0; i < mutex_cnt; i++) {
19
+		cds_mutex_init(&g->mutexes[i]);
21 20
 	}
22
-	return -1;
21
+	g->mutex_to_assign = 0;
22
+	g->mutex_cnt = mutex_cnt;
23
+
24
+	return g;
23 25
 }
24 26
 
25
-void reference_counter_cleanup()
27
+void free_reference_counter_group(reference_counter_group_t *grp)
26 28
 {
27
-	if (ref_cntr_mutex) {
28
-		cds_mutex_destroy(ref_cntr_mutex);
29
-		cds_free((void*)ref_cntr_mutex);
30
-		ref_cntr_mutex = NULL;
29
+	int i;
30
+	if (grp) {
31
+		for (i = 0; i < grp->mutex_cnt; i++) {
32
+			cds_mutex_destroy(&grp->mutexes[i]);
33
+		}
34
+		cds_free(grp);
31 35
 	}
32 36
 }
33 37
 
34 38
 /* -------------------------------------------------------------------- */
35 39
 
36
-void init_reference_counter(reference_counter_data_t *ref)
40
+void init_reference_counter(reference_counter_group_t *grp, reference_counter_data_t *ref)
37 41
 {
38
-	if (ref) {
42
+	int m;
43
+	if (ref && grp) {
44
+		m = grp->mutex_to_assign;
39 45
 		ref->cntr = 1;
40
-		ref->mutex = ref_cntr_mutex;
46
+		ref->mutex = grp->mutexes + m;
47
+		m = (m + 1) % grp->mutex_cnt; /* can't be less than zero */
48
+		grp->mutex_to_assign = m;
41 49
 	}
42 50
 }
43 51
 
... ...
@@ -5,36 +5,151 @@
5 5
 extern "C" {
6 6
 #endif
7 7
 
8
+/** \ingroup cds
9
+ * @{ 
10
+ *
11
+ * \defgroup cds_ref_cnt Reference counting
12
+ *
13
+ * Experimental functions for reference counting (to simplify
14
+ * this code elsewhere).
15
+ * 
16
+ * Reference counter (\ref reference_counter_data_t) holds integer number which
17
+ * should be changed using functions \ref add_reference and \ref
18
+ * remove_reference. Using these functions is the number read/changed from
19
+ * locked section guarded by one mutex from a set of 'group mutexes'.
20
+ *
21
+ * Often used scenario:
22
+ * - list of structures, change in the list (adding, removing) is guarded by
23
+ *   one mutex, 
24
+ * - each structure has reference counter, when the count is zero, the
25
+ *   structure can be removed from the list and freed
26
+ *
27
+ * Note that mutex for adding references is needed because references are not
28
+ * added from critical section guarded by main list mutex but can be added whenever.
29
+ *
30
+ * Typical usage:
31
+ * \code
32
+ * struct some_structure {
33
+ *     ...
34
+ *     reference_counter_data_t ref;
35
+ *     ...
36
+ * };
37
+ *
38
+ * reference_counter_group_t *some_grp;
39
+ * 
40
+ * void init()
41
+ * {
42
+ *     some_grp = init_reference_counter_group(16);
43
+ * }
44
+ *
45
+ * void destroy()
46
+ * {
47
+ *     free_reference_counter_group(some_grp);
48
+ * }
49
+ * 
50
+ * ...
51
+ *
52
+ * // adding a member:
53
+ * struct some_struct *ss = malloc(...);
54
+ * init_reference_counter(some_grp, &ss->ref);
55
+ * lock(list);
56
+ * add_to_list(ss);
57
+ * unlock(list);
58
+ *
59
+ * ...
60
+ * // adding a reference doesn't need to lock list
61
+ * // can be done only by a reference owner
62
+ * add_reference(&ss->ref);
63
+ *
64
+ * // releasing a member when not needed for caller and there is
65
+ * // no way how to obtain reference for released member
66
+ * // (no way how to find a member in list)
67
+ * if (remove_reference(&ss->ref)) {
68
+ *     // last reference removed
69
+ *     lock(list);
70
+ *     remove_from_list(ss);
71
+ *     free(ss);
72
+ *     unlock(list);
73
+ * }
74
+ *  
75
+ * // or 
76
+ * // releasing a member when not needed for caller and it is possible 
77
+ * // to 'search' in the list (reference to released member can be somehow 
78
+ * // obtained by inspecting the list):
79
+ * lock(list);
80
+ * if (remove_reference(&ss->ref)) {
81
+ *     // last reference removed
82
+ *     remove_from_list(ss);
83
+ *     free(ss);
84
+ * } 
85
+ * unlock(list);
86
+ * \endcode
87
+ *
88
+ * \todo use atomic operations instead of locking
89
+ * @{ */
90
+
8 91
 #include <cds/sync.h>
9
-	
92
+
93
+/** Structure holding reference counter value. */
10 94
 typedef struct {
11
-	int cntr;
12
-	cds_mutex_t *mutex;
95
+	int cntr; /**< counter value */
96
+	cds_mutex_t *mutex; /**< mutex asigned to this reference counter */
13 97
 } reference_counter_data_t;
14
-	
15
-/* these functions can be called only by owner of at least one reference */
16
-/* owner is somebody who:
17
- *    - created a referenced structure
18
- *    - added a reference
19
- *    - got a reference by an ovner
20
- */
21
-
22
-void init_reference_counter(reference_counter_data_t *ref);
98
+
99
+/** Structure holding information about group of reference counters.  
100
+ * It holds array of mutexes which are assigned to single reference 
101
+ * counters in this group. The assignment is done using an operation 
102
+ * on pointer to reference_counter_data_t. */
103
+typedef struct {
104
+	int mutex_cnt; /**< number of mutexes for this group */
105
+
106
+	/** number of next mutex to be assigned - this member is NOT 
107
+	 * read/changed from critical section but it doesn't matter
108
+	 * if it will be rewritten between processes because it is used
109
+	 * for load distributing only (the worst thing that can happen
110
+	 * is that the same mutex is assigned twice) */
111
+	int mutex_to_assign; 
112
+	cds_mutex_t mutexes[1]; /**< array of mutexes (allocated together with the structure)*/
113
+} reference_counter_group_t;
114
+
115
+/** Initializes reference counter - sets its value to 1. 
116
+ * After call to this function, the caller is owner of first 
117
+ * reference. From now it can call other functions like
118
+ * \ref add_reference or \ref remove_reference. 
119
+ *
120
+ * This function initializes the mutex - it chooses one from 
121
+ * group mutexes. The mutex can not be changed after this 
122
+ * call (it is only for reading). */
123
+void init_reference_counter(reference_counter_group_t *grp, reference_counter_data_t *ref);
124
+
125
+/** Adds reference - increments reference counter.
126
+ * This function can be called only by owner of at least one reference! */
23 127
 void add_reference(reference_counter_data_t *ref);
128
+
129
+/** Returns the value of reference counter. This function is mostly
130
+ * useless. */
24 131
 int get_reference_count(reference_counter_data_t *ref);
25 132
 
26
-/* returns:
27
- * 0 if reference removed, but exist other references
28
- * 1 if removed last refernce and the element SHOULD be freed
133
+/** Removes reference - decrements reference counter.
134
+ * This function can be called only by owner of at least one reference!
29 135
  *
30
- * usage:
31
- * 
32
- * some_structure *ss;
33
- * ...
34
- * if (remove_reference(&ss->ref)) cds_free(&ss->ref);
136
+ * \retval 0 if reference removed, but other references exist
137
+ * \retval 1 if removed last reference
35 138
  *  */
36 139
 int remove_reference(reference_counter_data_t *ref);
37 140
 
141
+/** Creates and initializes group of reference counters. All reference 
142
+ * counters 'belonging' to this group are using the same set of mutexes. */
143
+reference_counter_group_t *create_reference_counter_group(int mutex_cnt);
144
+
145
+/** Destroys all resources used by reference counter group.
146
+ * After this function call no reference counter initialized
147
+ * by this group can be used. */
148
+void free_reference_counter_group(reference_counter_group_t *grp);
149
+
150
+/** @} 
151
+ * @} */
152
+
38 153
 #ifdef __cplusplus
39 154
 }
40 155
 #endif