Events in D

bitwise via Digitalmars-d digitalmars-d at puremagic.com
Thu Aug 31 09:55:04 PDT 2017


On Tuesday, 29 August 2017 at 05:10:25 UTC, bitwise wrote:
> I needed some C# style events, so I rolled my own.

The following is my current event implementation. I was able to 
make it thread safe by including an optional spin-lock. Of 
course, that extra spinlock has to be included in every single 
event, which has a pointlessly high memory cost, even when no 
handlers are attached to the event. Also, having this event call 
it's host class back when events are added/removed would require 
even MORE wasted memory by storing extra delegates. I've 
thoroughly explored the idea of a library-implemented event, and 
the downsides are not fixable.


struct Event(Handler, bool atomic = false)
     if(is(Handler == delegate) && is(ReturnType!Handler == void))
{
     Handler[] _handlers;

     static if(atomic)
     {
         Spinlock _lock;
         @disable this(this);
     }

     ref auto opOpAssign(string op, H)(H handler)
         if(op == "+")
     {
         static if(atomic) auto lk = lock(_lock);
         _handlers ~= toDelegate(handler);
         return this;
     }

     ref auto opOpAssign(string op, H)(H handler)
         if(op == "-")
     {
         static if(atomic) auto lk = lock(_lock);

         auto del = toDelegate(handler);
         foreach(ref handler; _handlers)
         {
             if(handler == del) {
                 _handlers = _handlers.remove(&handler - 
_handlers.ptr);
                 break;
             }
         }

         return this;
     }

     void opCall()(Parameters!Handler args)
     {
         static if(atomic)
         {
             Handler[] tmp;

             if(_handlers.length <= 64)
             {
                 auto lk = lock(_lock);
                 size_t sz = _handlers.length * Handler.sizeof;
                 tmp = cast(Handler[])(alloca(sz)[0..sz]);
                 tmp[] = _handlers[];
             }
             else
             {
                 auto lk = lock(_lock);
                 tmp = _handlers.dup;
             }

             foreach(ref handler; tmp)
                 handler(args);
         }
         else
         {
             foreach(ref handler; _handlers)
                 handler(args);
         }
     }

     bool opCast(T : bool)() {
         static if(atomic) auto lk = lock(_lock);
         return _handlers.length != 0;
     }

     void clear() {
         static if(atomic) auto lk = lock(_lock);
         _handlers.length = 0;
     }

     bool empty() {
         static if(atomic) auto lk = lock(_lock);
         return _handlers.length == 0;
     }
}



More information about the Digitalmars-d mailing list