-
Notifications
You must be signed in to change notification settings - Fork 171
/
ConditionVariable.c
161 lines (135 loc) · 5.68 KB
/
ConditionVariable.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
#include "moar.h"
/* This representation's function pointer table. */
static const MVMREPROps ConditionVariable_this_repr;
/* Creates a new type object of this representation, and associates it with
* the given HOW. */
static MVMObject * type_object_for(MVMThreadContext *tc, MVMObject *HOW) {
MVMSTable *st = MVM_gc_allocate_stable(tc, &ConditionVariable_this_repr, HOW);
MVMROOT(tc, st, {
MVMObject *obj = MVM_gc_allocate_type_object(tc, st);
MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, obj);
st->size = sizeof(MVMConditionVariable);
});
return st->WHAT;
}
/* Copies the body of one object to another. */
static void copy_to(MVMThreadContext *tc, MVMSTable *st, void *src, MVMObject *dest_root, void *dest) {
MVM_exception_throw_adhoc(tc, "Cannot copy object with representation ConditionVariable");
}
/* Called by the VM to mark any GCable items. */
static void gc_mark(MVMThreadContext *tc, MVMSTable *st, void *data, MVMGCWorklist *worklist) {
MVMConditionVariableBody *cv = (MVMConditionVariableBody *)data;
MVM_gc_worklist_add(tc, worklist, &cv->mutex);
}
/* Called by the VM in order to free memory associated with this object. */
static void gc_free(MVMThreadContext *tc, MVMObject *obj) {
MVMConditionVariable *cv = (MVMConditionVariable *)obj;
if (cv->body.condvar) {
uv_cond_destroy(cv->body.condvar);
MVM_free(cv->body.condvar);
cv->body.condvar = NULL;
}
}
static const MVMStorageSpec storage_spec = {
MVM_STORAGE_SPEC_REFERENCE, /* inlineable */
0, /* bits */
0, /* align */
MVM_STORAGE_SPEC_BP_NONE, /* boxed_primitive */
0, /* can_box */
0, /* is_unsigned */
};
/* Gets the storage specification for this representation. */
static const MVMStorageSpec * get_storage_spec(MVMThreadContext *tc, MVMSTable *st) {
return &storage_spec;
}
/* Compose the representation. */
static void compose(MVMThreadContext *tc, MVMSTable *st, MVMObject *info) {
/* Nothing to do for this REPR. */
}
/* Set the size of the STable. */
static void deserialize_stable_size(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) {
st->size = sizeof(MVMConditionVariable);
}
/* Initializes the representation. */
const MVMREPROps * MVMConditionVariable_initialize(MVMThreadContext *tc) {
return &ConditionVariable_this_repr;
}
static const MVMREPROps ConditionVariable_this_repr = {
type_object_for,
MVM_gc_allocate_object,
NULL, /* initialize */
copy_to,
MVM_REPR_DEFAULT_ATTR_FUNCS,
MVM_REPR_DEFAULT_BOX_FUNCS,
MVM_REPR_DEFAULT_POS_FUNCS,
MVM_REPR_DEFAULT_ASS_FUNCS,
MVM_REPR_DEFAULT_ELEMS,
get_storage_spec,
NULL, /* change_type */
NULL, /* serialize */
NULL, /* deserialize */
NULL, /* serialize_repr_data */
NULL, /* deserialize_repr_data */
deserialize_stable_size,
gc_mark,
gc_free,
NULL, /* gc_cleanup */
NULL, /* gc_mark_repr_data */
NULL, /* gc_free_repr_data */
compose,
NULL, /* spesh */
NULL, /* jit */
"ConditionVariable", /* name */
MVM_REPR_ID_ConditionVariable,
NULL, /* unmanaged_size */
NULL, /* describe_refs */
};
/* Given a reentrant mutex, produces an associated condition variable. */
MVMObject * MVM_conditionvariable_from_lock(MVMThreadContext *tc, MVMReentrantMutex *lock, MVMObject *type) {
MVMConditionVariable *cv;
int init_stat;
if (REPR(type)->ID != MVM_REPR_ID_ConditionVariable)
MVM_exception_throw_adhoc(tc, "Condition variable must have ConditionVariable REPR");
MVMROOT(tc, lock, {
cv = (MVMConditionVariable *)MVM_gc_allocate_object(tc, STABLE(type));
});
cv->body.condvar = MVM_malloc(sizeof(uv_cond_t));
if ((init_stat = uv_cond_init(cv->body.condvar)) < 0)
MVM_exception_throw_adhoc(tc, "Failed to initialize condition variable: %s",
uv_strerror(init_stat));
MVM_ASSIGN_REF(tc, &(cv->common.header), cv->body.mutex, (MVMObject *)lock);
return (MVMObject *)cv;
}
/* Adds the current thread to the queue of waiters on the condition variable,
* releasing, waiting, and then re-acquiring the lock. */
void MVM_conditionvariable_wait(MVMThreadContext *tc, MVMConditionVariable *cv) {
MVMReentrantMutex *rm = (MVMReentrantMutex *)cv->body.mutex;
AO_t orig_rec_level;
unsigned int interval_id;
if (MVM_load(&rm->body.holder_id) != tc->thread_id)
MVM_exception_throw_adhoc(tc,
"Can only wait on a condition variable when holding mutex");
interval_id = MVM_telemetry_interval_start(tc, "ConditionVariable.wait");
MVM_telemetry_interval_annotate((uintptr_t)cv->body.condvar, interval_id, "this condition variable");
orig_rec_level = MVM_load(&rm->body.lock_count);
MVM_store(&rm->body.holder_id, 0);
MVM_store(&rm->body.lock_count, 0);
MVMROOT2(tc, cv, rm, {
MVM_gc_mark_thread_blocked(tc);
uv_cond_wait(cv->body.condvar, rm->body.mutex);
MVM_gc_mark_thread_unblocked(tc);
});
MVM_store(&rm->body.holder_id, tc->thread_id);
MVM_store(&rm->body.lock_count, orig_rec_level);
MVM_telemetry_interval_stop(tc, interval_id, "ConditionVariable.wait");
}
/* Signals one thread waiting on the condition. */
void MVM_conditionvariable_signal_one(MVMThreadContext *tc, MVMConditionVariable *cv) {
MVM_telemetry_timestamp(tc, "ConditionVariable.signal_one");
uv_cond_signal(cv->body.condvar);
}
/* Signals all threads waiting on the condition. */
void MVM_conditionvariable_signal_all(MVMThreadContext *tc, MVMConditionVariable *cv) {
MVM_telemetry_timestamp(tc, "ConditionVariable.signal_all");
uv_cond_broadcast(cv->body.condvar);
}