aboutsummaryrefslogtreecommitdiff
path: root/event.go
blob: 63239070f6b8cd5c5ceae4b260490e1993748949 (plain) (blame)
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
package salticidae

// #include "salticidae/event.h"
// #include <signal.h>
import "C"
import "runtime"

// The C pointer type for an EventContext handle.
type CEventContext = *C.eventcontext_t
type eventContext struct {
	inner    CEventContext
	attached map[uintptr]interface{}
}

// The handle for an event loop.
type EventContext = *eventContext

func NewEventContext() EventContext {
	res := &eventContext{
		inner:    C.eventcontext_new(),
		attached: make(map[uintptr]interface{}),
	}
	runtime.SetFinalizer(res, func(self EventContext) { self.free() })
	return res
}

func (self EventContext) attach(ptr rawptr_t, x interface{}) { self.attached[uintptr(ptr)] = x }
func (self EventContext) detach(ptr rawptr_t)                { delete(self.attached, uintptr(ptr)) }
func (self EventContext) free()                              { C.eventcontext_free(self.inner) }

// Start the event loop. This is a blocking call that will hand over the
// control flow to the infinite loop which triggers callbacks upon new events.
// The function will return when Stop() is called.
func (self EventContext) Dispatch() { C.eventcontext_dispatch(self.inner) }

// Stop the event loop. This function is typically called in a callback. Notice
// that all methods of an EventContext should be invoked by the same thread
// which logically owns the loop. To schedule code executed in the event loop
// of a particular thread, see ThreadCall.
func (self EventContext) Stop() { C.eventcontext_stop(self.inner) }

// The C pointer type for a ThreadCall handle.
type CThreadCall = *C.threadcall_t
type threadCall struct{ inner CThreadCall }

// The handle for scheduling a function call executed by a particular
// EventContext event loop.
type ThreadCall = *threadCall

// The C function pointer type which takes threadcall_handle_t* and void*
// (passing in the custom user data allocated by C.malloc) as parameters.
type ThreadCallCallback = C.threadcall_callback_t

// Create a ThreadCall handle attached to the given EventContext. Each
// invokcation of AsyncCall() will schedule a function call executed in the
// given EventContext event loop.
func NewThreadCall(ec EventContext) ThreadCall {
	res := &threadCall{inner: C.threadcall_new(ec.inner)}
	runtime.SetFinalizer(res, func(self ThreadCall) { self.free() })
	return res
}

func (self ThreadCall) free() { C.threadcall_free(self.inner) }

// Schedule a function to be executed in the target event loop.
func (self ThreadCall) AsyncCall(callback ThreadCallCallback, userdata rawptr_t) {
	C.threadcall_async_call(self.inner, callback, userdata)
}

// The C pointer type for TimerEvent handle.
type CTimerEvent = *C.timerev_t
type timerEvent struct {
	inner CTimerEvent
	ec    EventContext
}

// The handle for a timed event.
type TimerEvent = *timerEvent

// The C function pointer type which takes timerev_t* (the C pointer to
// TimerEvent) and void* (the unsafe pointer to any userdata) as parameter.
type TimerEventCallback = C.timerev_callback_t

// Create a TimerEvent handle attached to the given EventContext, with a
// registered callback.
func NewTimerEvent(_ec EventContext, cb TimerEventCallback, userdata rawptr_t) TimerEvent {
	res := &timerEvent{
		inner: C.timerev_new(_ec.inner, cb, userdata),
		ec:    _ec,
	}
	_ec.attach(rawptr_t(res.inner), res)
	runtime.SetFinalizer(res, func(self TimerEvent) { self.free() })
	return res
}

func (self TimerEvent) free() { C.timerev_free(self.inner) }

// Change the callback.
func (self TimerEvent) SetCallback(callback TimerEventCallback, userdata rawptr_t) {
	C.timerev_set_callback(self.inner, callback, userdata)
}

// Schedule the timer to go off after t_sec seconds.
func (self TimerEvent) Add(t_sec float64) { C.timerev_add(self.inner, C.double(t_sec)) }

// Unschedule the timer if it was scheduled. The timer could still be rescheduled
// by calling Add() afterwards.
func (self TimerEvent) Del() {
	self.ec.detach(rawptr_t(self.inner))
	C.timerev_del(self.inner)
}

// Empty the timer. It will be unscheduled and deallocated and its methods
// should never be called again.
func (self TimerEvent) Clear() {
	self.ec.detach(rawptr_t(self.inner))
	C.timerev_clear(self.inner)
}

// The C pointer type for a SigEvent.
type CSigEvent = *C.sigev_t
type sigEvent struct {
	inner CSigEvent
	ec    EventContext
}

// The handle for a UNIX signal event.
type SigEvent = *sigEvent

type SigEventCallback = C.sigev_callback_t

var (
	SIGTERM = C.SIGTERM
	SIGINT  = C.SIGINT
)

// Create a SigEvent handle attached to the given EventContext, with a
// registered callback.
func NewSigEvent(_ec EventContext, cb SigEventCallback, userdata rawptr_t) SigEvent {
	res := &sigEvent{
		inner: C.sigev_new(_ec.inner, cb, userdata),
		ec:    _ec,
	}
	_ec.attach(rawptr_t(res.inner), res)
	runtime.SetFinalizer(res, func(self SigEvent) { self.free() })
	return res
}

func (self SigEvent) free() { C.sigev_free(self.inner) }

// Register the handle to listen for UNIX signal sig.
func (self SigEvent) Add(sig int) { C.sigev_add(self.inner, C.int(sig)) }

// Unregister the handle. The handle may be re-registered in the future.
func (self SigEvent) Del() {
	self.ec.detach(rawptr_t(self.inner))
	C.sigev_del(self.inner)
}

// Unregister the handle. Any methods of the handle should no longer be used
// and the handle will be deallocated.
func (self SigEvent) Clear() {
	self.ec.detach(rawptr_t(self.inner))
	C.sigev_clear(self.inner)
}

// The C pointer type for a MPSCQueue object.
type CMPSCQueue = *C.mpscqueue_t
type mpscQueue struct {
	inner CMPSCQueue
	ec    EventContext
}

// The object for a Multi-Producer, Single-Consumer queue.
type MPSCQueue = *mpscQueue

// The C function pointer type which takes mpscqueue_t* (the C pointer to
// MPSCQueue) and void* (the unsafe pointer to any userdata) as parameter, and
// returns either true (partial read from the queue, so it should be scheduled
// again), or false (the queue is drained, e.g. TryDequeue returns false).
type MPSCQueueCallback = C.mpscqueue_callback_t

// Create a MPSCQueue object.
func NewMPSCQueue() MPSCQueue {
	res := &mpscQueue{inner: C.mpscqueue_new(), ec: nil}
	runtime.SetFinalizer(res, func(self MPSCQueue) { self.free() })
	return res
}

func (self MPSCQueue) free() { C.mpscqueue_free(self.inner) }

// Register the callback invoked when there are new elements in the MPSC queue.
func (self MPSCQueue) RegHandler(_ec EventContext, callback MPSCQueueCallback, userdata rawptr_t) {
	C.mpscqueue_reg_handler(self.inner, _ec.inner, callback, userdata)
	self.ec = _ec
	_ec.attach(rawptr_t(self.inner), self)
}

// Unregister the callback.
func (self MPSCQueue) UnregHandler() {
	self.ec.detach(rawptr_t(self.inner))
	C.mpscqueue_unreg_handler(self.inner)
}

// Enqueue an element (thread-safe). It returns true if successful. If
// unbounded is true the queue is expanded when full (and this function will
// return true).
func (self MPSCQueue) Enqueue(elem rawptr_t, unbounded bool) bool {
	return bool(C.mpscqueue_enqueue(self.inner, elem, C.bool(unbounded)))
}

// Try to dequeue an element from the queue (should only be called from one
// thread). It returns true if successful.
func (self MPSCQueue) TryDequeue(elem *rawptr_t) bool {
	return bool(C.mpscqueue_try_dequeue(self.inner, elem))
}

// Set the initial capacity of the queue. This should only be called before the
// first dequeue/enqueue operation.
func (self MPSCQueue) SetCapacity(capacity int) {
	C.mpscqueue_set_capacity(self.inner, C.size_t(capacity))
}