diff --git a/source/vibe/core/core.d b/source/vibe/core/core.d index f8f57dd..ef13d6d 100644 --- a/source/vibe/core/core.d +++ b/source/vibe/core/core.d @@ -373,6 +373,13 @@ package Task runTask_internal(alias TFI_SETUP)() f.bumpTaskCounter(); auto handle = f.task(); + debug if (TaskFiber.ms_taskCreationCallback) { + TaskCreationInfo info; + info.handle = handle; + info.functionPointer = () @trusted { return cast(void*)f.m_taskFunc.functionPointer; } (); + () @trusted { TaskFiber.ms_taskCreationCallback(info); } (); + } + debug if (TaskFiber.ms_taskEventCallback) { () @trusted { TaskFiber.ms_taskEventCallback(TaskEvent.preStart, handle); } (); } @@ -916,6 +923,21 @@ void setTaskEventCallback(TaskEventCallback func) debug TaskFiber.ms_taskEventCallback = func; } +/** + Sets a callback that is invoked whenever new task is created. + + The callback is guaranteed to be invoked before the one set by + `setTaskEventCallback` for the same task handle. + + This function is useful mostly for implementing debuggers that + analyze the life time of tasks, including task switches. Note that + the callback will only be called for debug builds. +*/ +void setTaskCreationCallback(TaskCreationCallback func) +{ + debug TaskFiber.ms_taskCreationCallback = func; +} + /** A version string representing the current vibe version diff --git a/source/vibe/core/task.d b/source/vibe/core/task.d index 8733375..0d5230f 100644 --- a/source/vibe/core/task.d +++ b/source/vibe/core/task.d @@ -262,7 +262,13 @@ enum TaskEvent { fail /// Ended with an exception } +struct TaskCreationInfo { + Task handle; + const(void)* functionPointer; +} + alias TaskEventCallback = void function(TaskEvent, Task) nothrow; +alias TaskCreationCallback = void function(ref TaskCreationInfo) nothrow @safe; /** The maximum combined size of all parameters passed to a task delegate @@ -314,6 +320,7 @@ final package class TaskFiber : Fiber { package TaskFuncInfo m_taskFunc; package __gshared size_t ms_taskStackSize = defaultTaskStackSize; package __gshared debug TaskEventCallback ms_taskEventCallback; + package __gshared debug TaskCreationCallback ms_taskCreationCallback; this() @trusted nothrow { @@ -323,9 +330,7 @@ final package class TaskFiber : Fiber { static TaskFiber getThis() @safe nothrow { - auto f = () @trusted nothrow { - return Fiber.getThis(); - } (); + auto f = () @trusted nothrow { return Fiber.getThis(); } (); if (auto tf = cast(TaskFiber)f) return tf; if (!ms_globalDummyFiber) ms_globalDummyFiber = new TaskFiber; return ms_globalDummyFiber; @@ -508,6 +513,7 @@ package struct TaskFuncInfo { void function(ref TaskFuncInfo) func; void[2*size_t.sizeof] callable; void[maxTaskParameterSize] args; + debug ulong functionPointer; void set(CALLABLE, ARGS...)(ref CALLABLE callable, ref ARGS args) { @@ -525,6 +531,8 @@ package struct TaskFuncInfo { "The arguments passed to run(Worker)Task must not exceed "~ maxTaskParameterSize.to!string~" bytes in total size: "~TARGS.sizeof.stringof~" bytes"); + debug functionPointer = callPointer(callable); + static void callDelegate(ref TaskFuncInfo tfi) { assert(tfi.func is &callDelegate, "Wrong callDelegate called!?"); @@ -584,6 +592,16 @@ package struct TaskFuncInfo { } } +private ulong callPointer(C)(ref C callable) +@trusted nothrow @nogc { + alias IP = ulong; + static if (is(C == function)) return cast(IP)cast(void*)callable; + else static if (is(C == delegate)) return cast(IP)callable.funcptr; + else static if (is(typeof(&callable.opCall) == function)) return cast(IP)cast(void*)&callable.opCall; + else static if (is(typeof(&callable.opCall) == delegate)) return cast(IP)(&callable.opCall).funcptr; + else return cast(IP)&callable; +} + package struct TaskScheduler { import eventcore.driver : ExitReason; import eventcore.core : eventDriver;