1 module libasync.signal;
2 import std.traits;
3 
4 import libasync.types;
5 import libasync.events;
6 import core.thread;
7 
8 /// Enqueues a signal in the event loop of the AsyncSignal owner's thread, 
9 /// which allows a foreign thread to trigger the callback handler safely.
10 shared final class AsyncSignal
11 {
12 	private void delegate() m_sgh;
13 nothrow:
14 private:
15 	Thread m_owner;
16 	EventLoop m_evLoop;
17 	fd_t m_evId;
18 
19 public:
20 
21 	this(EventLoop evl) 
22 	in {
23 		assert(evl !is null);
24 	}
25 	body {
26 		m_evLoop = cast(shared) evl;
27 		import core.thread : Thread;
28 		m_owner = cast(shared) Thread.getThis();
29 
30 		version(Posix) {
31 			static if (EPOLL) {
32 				import core.sys.posix.pthread : pthread_self;
33 				m_pthreadId = cast(shared)pthread_self();
34 			} else /* if KQUEUE */ {
35 				m_owner_id = cast(shared) g_threadId;
36 			}
37 		}
38 	}
39 
40 	@property bool hasError() const 
41 	{
42 		return (cast(EventLoop)m_evLoop).status.code != Status.OK;
43 	}
44 
45 	/// Used to diagnose errors when run() or kill() returns false
46 	@property StatusInfo status() const {
47 		return (cast(EventLoop)m_evLoop).status;
48 	}
49 
50 	/// Human-readable string describing the error
51 	@property string error() const {
52 		return (cast(EventLoop)m_evLoop).error;
53 	}
54 
55 	/// Registers the signal handler in the event loop
56 	bool run(void delegate() del) 
57 	in {
58 		debug assert(Thread.getThis() is cast(Thread)m_owner);
59 	}
60 	body {
61 
62 		m_sgh = cast(void delegate()) del;
63 
64 		m_evId = (cast(EventLoop) m_evLoop).run(this);
65 		if (m_evId != fd_t.init)
66 			return true;
67 		else
68 			return false;
69 	}
70 
71 	/// Cleans up underlying resources. This object must be run() again afterwards to be valid
72 	bool kill() 
73 	in {
74 		debug assert(Thread.getThis() is cast(Thread)m_owner);
75 	}
76 	body {
77 		return (cast(EventLoop)m_evLoop).kill(cast(shared AsyncSignal) this);
78 	}
79 
80 	/// Triggers the handler in its local thread
81 	synchronized bool trigger(EventLoop evl) {
82 		return evl.notify(m_evId, this);
83 	}
84 
85 	/// ditto
86 	synchronized bool trigger() {
87 		return (cast(EventLoop)m_evLoop).notify(m_evId, this);
88 	}
89 
90 	/// Returns the Thread that created this object.
91 	synchronized @property Thread owner() const {
92 		return cast(Thread) m_owner;
93 	}
94 
95 	@property fd_t id() const {
96 		return m_evId;
97 	}
98 
99 package:
100 	version(Posix) mixin EvInfoMixinsShared;
101 
102 	void handler() {
103 		try m_sgh();
104 		catch {}
105 		return;
106 	}
107 }
108 
109 package shared struct SignalHandler {
110 	AsyncSignal ctxt;
111 	void function(shared AsyncSignal) fct;
112 
113 	void opCall(shared AsyncSignal ctxt) {
114 		assert(ctxt !is null);
115 		fct(ctxt);
116 		return;
117 	}
118 }
119 
120 
121 /**
122 	Determines if the given list of types has any non-immutable and unshared aliasing outside of their object tree.
123 
124 	The types in particular may only contain plain data, pointers or arrays to immutable or shared data, or references
125 	encapsulated in stdx.typecons.Isolated. Values that do not have unshared and unisolated aliasing are safe to be passed
126 	between threads.
127 */
128 template isWeaklyIsolated(T...)
129 {
130 	import std.typecons : Rebindable;
131 	static if (T.length == 0) enum bool isWeaklyIsolated = true;
132 	else static if (T.length > 1) enum bool isWeaklyIsolated = isWeaklyIsolated!(T[0 .. $/2]) && isWeaklyIsolated!(T[$/2 .. $]);
133 	else {
134 		static if(is(T[0] == immutable)) enum bool isWeaklyIsolated = true;
135 		else static if (is(T[0] == shared)) enum bool isWeaklyIsolated = true;
136 		else static if (isInstanceOf!(Rebindable, T[0])) enum bool isWeaklyIsolated = isWeaklyIsolated!(typeof(T[0].get()));
137 		else static if (is(T[0] : Throwable)) enum bool isWeaklyIsolated = true; // WARNING: this is unsafe, but needed for send/receive!
138 		else static if (is(typeof(T[0].__isIsolatedType))) enum bool isWeaklyIsolated = true;
139 		else static if (is(typeof(T[0].__isWeakIsolatedType))) enum bool isWeaklyIsolated = true;
140 		else static if (is(T[0] == class)) enum bool isWeaklyIsolated = false;
141 		else static if (is(T[0] == interface)) enum bool isWeaklyIsolated = false; // can't know if the implementation is isolated
142 		else static if (is(T[0] == delegate)) enum bool isWeaklyIsolated = T[0].stringof.endsWith(" shared"); // can't know to what a delegate points - FIXME: use something better than a string comparison
143 		else static if (isDynamicArray!(T[0])) enum bool isWeaklyIsolated = is(typeof(T[0].init[0]) == immutable);
144 		else static if (isAssociativeArray!(T[0])) enum bool isWeaklyIsolated = false; // TODO: be less strict here
145 		else static if (isSomeFunction!(T[0])) enum bool isWeaklyIsolated = true; // functions are immutable
146 		else static if (isPointer!(T[0])) enum bool isWeaklyIsolated = is(typeof(*T[0].init) == immutable) || is(typeof(*T[0].init) == shared);
147 		else static if (isAggregateType!(T[0])) enum bool isWeaklyIsolated = isWeaklyIsolated!(FieldTypeTuple!(T[0]));
148 		else enum bool isWeaklyIsolated = true;
149 	}
150 }