1 /******************************************************************************
2 
3     Task extension which makes it possible to store exceptions from a callback
4     so that is gets thrown after the task is resumed. This is useful for
5     providing meaningful stacktraces which point to the origin of the faulty
6     operation rather than to the event loop context.
7 
8     Usage example:
9         See the documented unittest of the ExceptionForwarding struct
10 
11     Copyright:
12         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
13         All rights reserved.
14 
15     License:
16         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
17         Alternatively, this file may be distributed under the terms of the Tango
18         3-Clause BSD License (see LICENSE_BSD.txt for details).
19 
20 ******************************************************************************/
21 
22 module ocean.task.extensions.ExceptionForwarding;
23 
24 version (unittest)
25 {
26     import ocean.core.Test;
27     import ocean.task.Task;
28     import core.thread;
29 }
30 
31 /******************************************************************************
32 
33     Task extension to be used with the `TaskWith` class.
34 
35 ******************************************************************************/
36 
37 struct ExceptionForwarding
38 {
39     public Exception to_throw;
40 
41     void onResumed ( )
42     {
43         if (this.to_throw !is null)
44         {
45             // reset the reference so it won't throw again
46             // if the same fiber handles an exception and suspends again
47             auto to_throw = this.to_throw;
48             this.to_throw = null;
49             throw to_throw;
50         }
51     }
52 }
53 
54 ///
55 unittest
56 {
57     class ExceptionExternal : Exception
58     {
59         this ( )
60         {
61             super("external");
62         }
63     }
64 
65     class MyTask : TaskWith!(ExceptionForwarding)
66     {
67         bool caught = false;
68 
69         override protected void run ( )
70         {
71             try
72             {
73                 // when the following call to `this.suspend()` exit (== after
74                 // resuming by a callback or the scheduler), the stored
75                 // exception (if any) will be thrown
76                 this.suspend();
77             }
78             catch (ExceptionExternal e)
79             {
80                 caught = true;
81             }
82         }
83     }
84 
85     // create a task and assign it to a worker fiber
86     auto task = new MyTask;
87     task.assignTo(new WorkerFiber(10240));
88 
89     // will start the task (which then yields immediately)
90     task.resume();
91 
92     // makes stored exception instance thrown from within the task when
93     // resumed
94     task.extensions.exception_forwarding.to_throw = new ExceptionExternal;
95     task.resume();
96     test(task.caught);
97 }