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