-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathresizable_array.c
170 lines (149 loc) · 4.86 KB
/
resizable_array.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#define _GNU_SOURCE
#include "resizable_array.h"
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include "lib.h"
#include "logger.h"
#include "sock_events.h"
typedef struct {
ELEM_TYPE elem;
pthread_mutex_t mutex;
} ElemWrapper;
static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
static ElemWrapper **array = NULL;
static int size = 0;
// Private functions
ElemWrapper **allocate_array(int _size) {
return (ElemWrapper **)my_calloc(sizeof(ElemWrapper *) * _size);
}
static bool init(int init_size) {
if (init_size < MIN_INIT_SIZE) init_size = MIN_INIT_SIZE;
LOG(INFO, "Resizable array initialized to size %d.", init_size);
if (!(array = allocate_array(init_size))) goto error;
size = init_size;
return true;
error:
LOG_FUNC_ERROR;
return false;
}
static bool double_size(int index) {
// Compute new size
int new_size, normal_new_size = size * GROWTH_FACTOR;
new_size = normal_new_size > index + 1 ? normal_new_size : index + 1;
LOG(INFO, "Resizable array doubling size to %d.", new_size);
ElemWrapper **new_a;
if (!(new_a = allocate_array(new_size))) goto error;
for (int i = 0; i < size; i++) new_a[i] = array[i];
free(array);
array = new_a;
size = new_size;
return true;
error:
LOG_FUNC_ERROR;
return false;
}
static bool is_index_in_bounds(int index) { return index < size; }
/* Public functions */
bool ra_put_elem(int index, ELEM_TYPE elem) {
pthread_rwlock_wrlock(&rwlock);
if (!array && !init(index + 1)) goto error;
if (index > size - 1 && !double_size(index)) goto error;
ElemWrapper *ew = (ElemWrapper *)my_malloc(sizeof(ElemWrapper));
mutex_init(&ew->mutex);
ew->elem = elem;
array[index] = ew;
pthread_rwlock_unlock(&rwlock);
return true;
error:
pthread_rwlock_unlock(&rwlock);
LOG_FUNC_ERROR;
return false;
}
ELEM_TYPE ra_get_and_lock_elem(int index) {
pthread_rwlock_rdlock(&rwlock);
if (!is_index_in_bounds(index)) goto error;
if (!array[index]) {
LOG(WARN, "Null in array at index %d.", index);
pthread_rwlock_unlock(&rwlock);
return NULL;
}
ElemWrapper *ew = array[index];
mutex_lock(&ew->mutex);
return ew->elem;
error:
LOG(ERROR, "OOB (index %d, bound %d).", index, size - 1);
pthread_rwlock_unlock(&rwlock);
LOG_FUNC_ERROR;
return NULL;
}
void ra_unlock_elem(int index) {
if (!is_index_in_bounds(index)) goto error1;
if (!array[index]) goto error2;
mutex_unlock(&(array[index]->mutex));
pthread_rwlock_unlock(&rwlock);
return;
error1:
LOG(ERROR, "OOB (index %d, bound %d).", index, size - 1);
goto error_out;
error2:
LOG(ERROR, "No item at index %d.", index);
error_out:
pthread_rwlock_unlock(&rwlock);
LOG_FUNC_ERROR;
}
ELEM_TYPE ra_remove_elem(int index) {
pthread_rwlock_wrlock(&rwlock);
if (!is_index_in_bounds(index)) goto error;
if (!array[index]) {
pthread_rwlock_unlock(&rwlock);
return NULL;
}
ElemWrapper *ew = array[index];
// No need to lock it. Having the rwlock in write mode means no other
// thread has a valid el or will be able to acquire one.
mutex_destroy(&ew->mutex);
ELEM_TYPE el = ew->elem;
array[index] = NULL;
free(ew);
pthread_rwlock_unlock(&rwlock);
return el;
error:
LOG(ERROR, "OOB (index %d, bound %d).", index, size - 1);
pthread_rwlock_unlock(&rwlock);
LOG_FUNC_ERROR;
return NULL;
}
bool ra_is_present(int index) {
pthread_rwlock_rdlock(&rwlock);
if (!is_index_in_bounds(index)) goto out_false;
bool ret = (array[index] != NULL);
pthread_rwlock_unlock(&rwlock);
return ret;
out_false:
pthread_rwlock_unlock(&rwlock);
return false;
}
int ra_get_size(void) {
pthread_rwlock_rdlock(&rwlock);
int ret = size;
pthread_rwlock_unlock(&rwlock);
return ret;
}
void ra_free() {
pthread_rwlock_rdlock(&rwlock);
for (int i = 0; i < size; i++) {
if (array[i]) {
// We don't check for errors on this one. This is called
// after fork() and will logically failed if the mutex
// was lock at the time of forking. This is normal.
pthread_mutex_destroy(&array[i]->mutex);
FREE_ELEM(array[i]->elem);
free(array[i]);
}
}
free(array);
pthread_rwlock_unlock(&rwlock);
pthread_rwlock_destroy(&rwlock);
}