number of threads will be running in parallel at the same time
28 |
29 | data lt_runnables type zif_executor_service=>tty_runnables.
30 | data(lo_fixed_pool) = zcl_executors=>new_fixed_thread_pool(
31 | iv_threads = 3
32 | io_callback = me ).
33 |
34 |
35 | "you can use submit( )...
36 | do 20 times.
37 | data(lo_runnable1) = new lcl_sum_numbers( value #(
38 | ( 10 ) ( 20 ) ( 30 )
39 | ) ).
40 |
41 | "A future represents the result of an async computation
42 | data(lo_future) = lo_fixed_pool->submit( lo_runnable1 ).
43 |
44 | append lo_runnable1 to lt_runnables.
45 |
46 |
47 | enddo.
48 |
49 | "...or you can use invoke all.
50 | data(lt_futures) = lo_fixed_pool->invoke_all( lt_runnables ).
51 |
52 | "A get in a future will make the running code to wait for the thread to finish
53 | data(lo_result) = cast lcl_sum_numbers( lo_future->get( ) ).
54 | assert_equals(
55 | exp = 60
56 | act = lo_result->get_sum( )
57 | ).
58 |
59 | "Waiting for a specific thread to complete
60 | lo_future = lt_futures[ 1 ].
61 | lo_result = cast lcl_sum_numbers( lo_future->get( ) ).
62 | assert_equals(
63 | exp = 60
64 | act = lo_result->get_sum( )
65 | ).
66 |
67 | "Waiting for all threads to complete
68 | lo_fixed_pool->await_termination( ).
69 |
70 | assert_equals(
71 | exp = 2400
72 | act = v_final_sum
73 | ).
74 |
75 |
76 | endmethod.
77 |
78 | method zif_thread_callback~on_result.
79 | data(lv_thread_sum) = cast lcl_sum_numbers( io_result )->get_sum( ).
80 | v_final_sum = v_final_sum + lv_thread_sum.
81 | endmethod.
82 |
83 | method zif_thread_callback~on_error.
84 | fail( 'not expected' ).
85 | endmethod.
86 |
87 | endclass.
88 |
--------------------------------------------------------------------------------
/src/example/ztc_thread_pool_example.clas.locals_imp.abap:
--------------------------------------------------------------------------------
1 | class lcl_sum_numbers definition create public.
2 |
3 | public section.
4 | types:
5 | tty_numbers type standard table of i
6 | with non-unique default key.
7 | interfaces: zif_runnable, zif_runnable_result.
8 | methods:
9 | constructor
10 | importing
11 | it_numbers type tty_numbers,
12 | get_sum
13 | returning value(rv_result) type i.
14 | protected section.
15 | private section.
16 | data t_numbers type lcl_sum_numbers=>tty_numbers.
17 | data v_result type i.
18 |
19 | endclass.
20 |
21 | class lcl_sum_numbers implementation.
22 |
23 | method constructor.
24 | t_numbers = it_numbers.
25 | endmethod.
26 | method zif_runnable~run.
27 | v_result = reduce #(
28 | init result type i
29 | for number in t_numbers
30 | next result = result + number
31 | ).
32 | ro_result = me.
33 | endmethod.
34 |
35 | method get_sum.
36 | rv_result = v_result.
37 | endmethod.
38 |
39 | endclass.
40 |
--------------------------------------------------------------------------------
/src/example/ztc_thread_pool_example.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZTC_THREAD_POOL_EXAMPLE
7 | E
8 | A simple thread example
9 | 05
10 | 1
11 | X
12 | X
13 | X
14 | 12
15 | 11
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/package.devc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Threads for ABAP
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/tests/package.devc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Test dummies
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/tests/zcl_deactivated_thread.clas.abap:
--------------------------------------------------------------------------------
1 | "! A thread for tests.
2 | "! You can use deactivated threads in conjunction with the
3 | "! deactivated thread factory.
4 | "! This thread allows for setting of results or errors, as well as
5 | "! returning the runnable for which it was called.
6 | "! This deactivated thread does not call the runnable.
7 | "! This deactivated thread immediately calls the callback object upon start.
8 | "!
9 | class zcl_deactivated_thread definition
10 | public
11 | inheriting from zcl_thread
12 | final
13 | create public
14 | for testing.
15 |
16 | public section.
17 | methods:
18 | "! Returns the runnable for which the Thread was created
19 | "! @parameter ro_result | Thread's runnable
20 | get_runnable
21 | returning value(ro_result) type ref to zif_runnable,
22 | "! Set the result which will be sent to the callback routine
23 | "! @parameter io_result | the result to be sent back
24 | set_result
25 | importing
26 | io_result type ref to zif_runnable_result,
27 | "! Set the error which will be sent to the callback routine
28 | "! @parameter io_error | the error to be sent back
29 | set_error
30 | importing
31 | io_error type ref to cx_root,
32 | "! Allows the verification if the thread was started
33 | "! @parameter r_result | ABAP_TRUE/ABAP_FALSE
34 | was_started
35 | returning value(r_result) type abap_bool,
36 | "! Deactivated thread constructor.
37 | "!
38 | "! @parameter io_runnable | Runnable
39 | "! @parameter io_callback | Callback routine
40 | "! @parameter iv_taskname | Task name
41 | constructor
42 | importing
43 | io_runnable type ref to zif_runnable
44 | io_callback type ref to zif_thread_callback optional
45 | iv_taskname type char32,
46 | get_result redefinition,
47 | on_end redefinition,
48 | start redefinition.
49 |
50 | protected section.
51 | private section.
52 | data:
53 | v_started type abap_bool,
54 | o_callback type ref to zif_thread_callback,
55 | o_runnable type ref to zif_runnable,
56 | o_result type ref to zif_runnable_result,
57 | o_error type ref to cx_root,
58 | v_taskname type char32.
59 | endclass.
60 |
61 |
62 |
63 | class zcl_deactivated_thread implementation.
64 |
65 | method get_runnable.
66 | ro_result = o_runnable.
67 | endmethod.
68 | method set_result.
69 | o_result = io_result.
70 | endmethod.
71 |
72 | method set_error.
73 | o_error = io_error.
74 | endmethod.
75 |
76 | method constructor.
77 | super->constructor(
78 | io_callback = io_callback
79 | io_runnable = io_runnable
80 | iv_taskname = iv_taskname
81 | ).
82 |
83 | o_callback = io_callback.
84 | o_runnable = io_runnable.
85 | v_taskname = iv_taskname.
86 |
87 | endmethod.
88 |
89 |
90 | method start.
91 | v_started = abap_true.
92 | on_end( v_taskname ).
93 | endmethod.
94 |
95 | method on_end.
96 |
97 | if o_callback is bound
98 | and o_result is bound.
99 | o_callback->on_result(
100 | iv_taskname = p_task
101 | io_result = o_result
102 | ).
103 | endif.
104 |
105 | if o_callback is bound
106 | and o_error is bound.
107 | o_callback->on_error(
108 | iv_taskname = p_task
109 | io_error = o_error
110 | ).
111 | endif.
112 |
113 |
114 | endmethod.
115 |
116 | method get_result.
117 | ro_result = o_result.
118 | endmethod.
119 |
120 | method was_started.
121 | r_result = me->v_started.
122 | endmethod.
123 |
124 |
125 | endclass.
126 |
--------------------------------------------------------------------------------
/src/tests/zcl_deactivated_thread.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_DEACTIVATED_THREAD
7 | E
8 | A thread for tests
9 | 05
10 | 1
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/tests/zcl_deactivated_thread_factory.clas.abap:
--------------------------------------------------------------------------------
1 | "! A deactivated thread factory
2 | "! This factory can be used for tests of code that require threads.
3 | "! It creates deactivated threads that do not run the runnable.
4 | "! It allows:
5 | "!
- for verification of created threads
6 | "! - set of threads result
.
7 | "!
8 | class zcl_deactivated_thread_factory definition
9 | public
10 | final
11 | create public
12 | for testing.
13 |
14 | public section.
15 | types:
16 | begin of sty_thread_result,
17 | thread_num type i,
18 | result type ref to zif_runnable_result,
19 | end of sty_thread_result,
20 | tty_threads_results type standard table of sty_thread_result
21 | with non-unique default key,
22 | tty_threads type standard table of ref to zcl_deactivated_thread
23 | with non-unique default key.
24 | interfaces zif_thread_factory .
25 | methods:
26 | "! Sets the result to be returned by the iv_thread_num(n) created by the factory.
27 | "!
28 | "! @parameter iv_thread_num | The thread creation number to have the result
29 | "! @parameter io_result | The result to be returned by the nth created thread.
30 | set_thread_call_result
31 | importing
32 | iv_thread_num type i
33 | io_result type ref to zif_runnable_result,
34 | get_created_threads
35 | returning value(rt_result) type tty_threads.
36 | protected section.
37 | private section.
38 | data:
39 | t_results type tty_threads_results,
40 | v_created_threads type i,
41 | t_created_threads type tty_threads.
42 | endclass.
43 |
44 |
45 |
46 | class zcl_deactivated_thread_factory implementation.
47 |
48 | method zif_thread_factory~new_thread.
49 |
50 | data(lo_thread) = new zcl_deactivated_thread(
51 | io_runnable = io_runnable
52 | io_callback = io_callback
53 | iv_taskname = iv_taskname
54 | ).
55 |
56 | ro_result = lo_thread.
57 |
58 | append lo_thread to t_created_threads.
59 |
60 | v_created_threads = v_created_threads + 1.
61 |
62 | try.
63 | data(lo_thread_result) = t_results[ thread_num = v_created_threads ]-result.
64 | lo_thread->set_result( lo_thread_result ).
65 | catch cx_sy_itab_line_not_found.
66 | lo_thread->set_result( new zcl_dummy_runnable_result( ) ).
67 | endtry.
68 |
69 | endmethod.
70 |
71 | method set_thread_call_result.
72 | append value #(
73 | thread_num = iv_thread_num
74 | result = io_result ) to t_results.
75 |
76 | endmethod.
77 |
78 | method get_created_threads.
79 | rt_result = t_created_threads.
80 | endmethod.
81 |
82 | endclass.
83 |
--------------------------------------------------------------------------------
/src/tests/zcl_deactivated_thread_factory.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_DEACTIVATED_THREAD_FACTORY
7 | E
8 | A deactivated thread factory
9 | 05
10 | 1
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/tests/zcl_runnable_dummy.clas.abap:
--------------------------------------------------------------------------------
1 | class zcl_runnable_dummy definition
2 | public
3 | final
4 | create public
5 | for testing.
6 |
7 | public section.
8 |
9 | interfaces: zif_runnable,zif_runnable_result.
10 | methods: constructor
11 | importing
12 | iv_wait type zethread_wait_time optional.
13 | methods:
14 | raise_on_run,
15 | was_run_called returning value(r_result) type abap_bool.
16 | protected section.
17 | private section.
18 | data v_run_called type abap_bool.
19 | data v_wait type zethread_wait_time.
20 | data v_raise_on_run type abap_bool.
21 | endclass.
22 |
23 |
24 |
25 | class zcl_runnable_dummy implementation.
26 |
27 | method constructor.
28 | v_wait = iv_wait.
29 | endmethod.
30 |
31 | method zif_runnable~run.
32 | v_run_called = abap_true.
33 | if v_wait is not initial.
34 | wait up to v_wait seconds.
35 | endif.
36 | if v_raise_on_run eq abap_true.
37 | "just an example, could be any no check exception
38 | raise exception type zcx_thread_start_fail.
39 | endif.
40 | ro_result = me.
41 | endmethod.
42 |
43 | method was_run_called.
44 | r_result = v_run_called.
45 | endmethod.
46 |
47 | method raise_on_run.
48 | v_raise_on_run = abap_true.
49 | endmethod.
50 |
51 |
52 | endclass.
53 |
--------------------------------------------------------------------------------
/src/tests/zcl_runnable_dummy.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_RUNNABLE_DUMMY
7 | E
8 | A dummy runnable
9 | 05
10 | 1
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/zcl_assert.clas.abap:
--------------------------------------------------------------------------------
1 | class zcl_assert definition
2 | for testing
3 | duration short
4 | risk level harmless
5 | public
6 | abstract
7 | create public .
8 |
9 | public section.
10 | class-methods:
11 | "! Abort test execution due to missing context
12 | "!
13 | "! @parameter msg | Description
14 | "! @parameter detail | Further description
15 | "! @parameter quit | Alter control flow/ quit test (METHOD, +++CLASS+++)
16 | abort
17 | importing !msg type csequence optional
18 | !detail type csequence optional
19 | !quit type int1 default if_Aunit_Constants=>class
20 | preferred parameter msg,
21 |
22 | "! Ensure the validity of the reference
23 | "!
24 | "! @parameter act | Reference variable to be checked
25 | "! @parameter msg | Description
26 | "! @parameter level | Severity (TOLERABLE, CRITICAL, FATAL)
27 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
28 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
29 | assert_Bound
30 | importing value(act) type any
31 | !msg type csequence optional
32 | !level type int1 default if_Aunit_Constants=>severity-medium
33 | !quit type int1 default if_Aunit_Constants=>quit-test
34 | returning value(assertion_Failed) type abap_Bool,
35 |
36 | "! Ensure that character string fits to simple pattern
37 | "!
38 | "! @parameter act | Actual Object
39 | "! @parameter exp | Expected Template
40 | "! @parameter msg | Message in Case of Error
41 | "! @parameter level | Severity (TOLERABLE, CRITICAL, FATAL)
42 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
43 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
44 | assert_Char_Cp
45 | importing !act type csequence
46 | !exp type csequence
47 | !msg type csequence optional
48 | !level type int1 default if_Aunit_Constants=>severity-medium
49 | !quit type int1 default if_Aunit_Constants=>quit-test
50 | returning value(assertion_Failed) type abap_Bool,
51 |
52 | "! Ensure that character string does not fit to simple pattern
53 | "!
54 | "! @parameter act | Actual text which shall not adhere to EXP pattern
55 | "! @parameter exp | Simple text pattern
56 | "! @parameter msg | Description
57 | "! @parameter level | Severity (TOLERABLE, CRITICAL, FATAL)
58 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
59 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
60 | assert_Char_Np
61 | importing value(act) type csequence
62 | !exp type csequence
63 | !msg type csequence optional
64 | !level type int1 default if_Aunit_Constants=>severity-medium
65 | !quit type int1 default if_Aunit_Constants=>quit-test
66 | returning value(assertion_Failed) type abap_Bool,
67 |
68 | "! Ensure difference between 2 elementary data objects
69 | "!
70 | "! @parameter act | Data object with current value
71 | "! @parameter exp | Compare object with unexpected value
72 | "! @parameter tol | Tolerance range for floating point comparison
73 | "! @parameter msg | Message in case of error
74 | "! @parameter level | Severity (TOLERABLE, CRITICAL, FATAL)
75 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
76 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
77 | assert_Differs
78 | importing value(act) type simple
79 | value(exp) type simple
80 | !tol type f optional
81 | !msg type csequence optional
82 | !level type int1 default if_Aunit_Constants=>severity-medium
83 | !quit type int1 default if_Aunit_Constants=>quit-test
84 | returning value(assertion_Failed) type abap_Bool,
85 |
86 | "! Ensure equality of two data objects
87 | "!
88 | "! @parameter act | Data object with current value
89 | "! @parameter exp | Data object with expected type
90 | "! @parameter ignore_Hash_Sequence | Ignore sequence in hash tables
91 | "! @parameter tol | Tolerance Range (for directly passed floating numbers)
92 | "! @parameter msg | Description
93 | "! @parameter level | Severity (TOLERABLE, CRITICAL, FATAL)
94 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
95 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
96 | assert_Equals
97 | importing value(act) type any
98 | value(exp) type any
99 | !ignore_Hash_Sequence type abap_Bool default abap_False
100 | !tol type f optional
101 | !msg type csequence optional
102 | !level type int1 default if_Aunit_Constants=>severity-medium
103 | !quit type int1 default if_Aunit_Constants=>quit-test
104 | returning value(assertion_Failed) type abap_Bool,
105 |
106 |
107 | "! Ensure approximate consistency of 2 floating point numbers
108 | "!
109 | "! @parameter act | Data object with current value
110 | "! @parameter exp | Data object with expected value
111 | "! @parameter rtol | Relative tolerance
112 | "! @parameter msg | Description
113 | "! @parameter level | Severity (TOLERABLE, CRITICAL, FATAL)
114 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
115 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
116 | assert_Equals_Float
117 | importing value(act) type numeric
118 | value(exp) type numeric
119 | !rtol type numeric default cl_Abap_Unit_Assert=>rtol_Default
120 | !msg type csequence optional
121 | !level type int1 default if_Aunit_Constants=>severity-medium
122 | !quit type int1 default if_Aunit_Constants=>quit-test
123 | returning value(assertion_Failed) type abap_Bool,
124 |
125 | "! Ensure that boolean equals ABAP_FALSE
126 | "!
127 | "! @parameter act | Actual data object
128 | "! @parameter msg | Description
129 | "! @parameter level | Severity (TOLERABLE, +CRITICAL+, FATAL)
130 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
131 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
132 | assert_False
133 | importing value(act) type abap_Bool
134 | !msg type csequence optional
135 | !level type int1 default if_Aunit_Constants=>severity-medium
136 | !quit type int1 default if_Aunit_Constants=>quit-test
137 | returning value(assertion_Failed) type abap_Bool,
138 |
139 | "! Ensure that data object value is initial
140 | "!
141 | "! @parameter act | Actual data object
142 | "! @parameter msg | Description
143 | "! @parameter level | Severity (TOLERABLE, +CRITICAL+, FATAL)
144 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
145 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
146 | assert_Initial
147 | importing value(act) type any default sy-subrc
148 | !msg type csequence optional
149 | !level type int1 default if_Aunit_Constants=>severity-medium
150 | !quit type int1 default if_Aunit_Constants=>quit-test
151 | preferred parameter act
152 | returning
153 | value(assertion_Failed) type abap_Bool,
154 |
155 | "! Ensure invalidity of the reference of a reference variable
156 | "!
157 | "! @parameter act | Reference variable to be checked
158 | "! @parameter msg | Description
159 | "! @parameter level | Severity (TOLERABLE, +CRITICAL+, FATAL)
160 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
161 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
162 | assert_Not_Bound
163 | importing value(act) type any
164 | !msg type csequence optional
165 | !level type int1 default if_Aunit_Constants=>severity-medium
166 | !quit type int1 default if_Aunit_Constants=>quit-test
167 | returning value(assertion_Failed) type abap_Bool,
168 |
169 | "! Ensure that value of data object is not initial
170 | "!
171 | "! @parameter act | Actual Data Object
172 | "! @parameter msg | Message in Case of Error
173 | "! @parameter level | Severity (TOLERABLE, +CRITICAL+, FATAL)
174 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
175 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
176 | assert_Not_Initial
177 | importing value(act) type any
178 | !msg type csequence optional
179 | !level type int1 default if_Aunit_Constants=>severity-medium
180 | !quit type int1 default if_Aunit_Constants=>quit-test
181 | returning
182 | value(assertion_Failed) type abap_Bool,
183 |
184 | "! Ensure that number is in given range
185 | "!
186 | "! @parameter lower | Upper boundary
187 | "! @parameter upper | Lower boundary
188 | "! @parameter number | Number expected to be within the boundaries
189 | "! @parameter msg | Description
190 | "! @parameter level | Alter control flow/ quit test (NO, +METHOD+, CLASS)
191 | "! @parameter quit | Control flow in case of failed assertion
192 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
193 | assert_Number_Between
194 | importing !lower type numeric
195 | !upper type numeric
196 | !number type numeric
197 | !msg type csequence optional
198 | !level type int1 default if_Aunit_Constants=>severity-medium
199 | !quit type int1 default if_Aunit_Constants=>quit-test
200 | returning value(assertion_Failed) type abap_Bool,
201 |
202 | "! Ensure specific value of return code
203 | "!
204 | "! @parameter exp | Expected return code, optional, if not zero
205 | "! @parameter act | Return code of ABAP statements
206 | "! @parameter msg | Description
207 | "! @parameter level | Severity (TOLERABLE, +CRITICAL+, FATAL)
208 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
209 | "! @parameter symsg | System message
210 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
211 | assert_Subrc
212 | importing value(exp) type sysubrc default 0
213 | value(act) type sysubrc default sy-subrc
214 | !msg type csequence optional
215 | !level type int1 default if_Aunit_Constants=>severity-medium
216 | !quit type int1 default if_Aunit_Constants=>quit-test
217 | !symsg type symsg optional
218 | preferred parameter act
219 | returning value(assertion_Failed) type abap_Bool,
220 |
221 | "! Ensure that data is contained as line within internal table
222 | "!
223 | "! @parameter line | Data Object that is typed like line of TABLE
224 | "! @parameter table | Internal Table that shall contain LINE
225 | "! @parameter msg | Description
226 | "! @parameter level | Severity (TOLERABLE, +CRITICAL+, FATAL)
227 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
228 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
229 | assert_Table_Contains
230 | importing value(line) type any
231 | !table type any table
232 | !msg type csequence optional
233 | !level type int1 default if_Aunit_Constants=>severity-medium
234 | !quit type int1 default if_Aunit_Constants=>quit-test
235 | returning value(assertion_Failed) type abap_Bool,
236 |
237 | "! Ensure that data is not contained as line in internal table
238 | "!
239 | "! @parameter line | Data Object that is typed like line of TABLE
240 | "! @parameter table | Internal Table that must not contain LINE
241 | "! @parameter msg | Description
242 | "! @parameter level | Severity (TOLERABLE, +CRITICAL+, FATAL)
243 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
244 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
245 | assert_Table_Not_Contains
246 | importing value(line) type any
247 | !table type any table
248 | !msg type csequence optional
249 | !level type int1 default if_Aunit_Constants=>severity-medium
250 | !quit type int1 default if_Aunit_Constants=>quit-test
251 | returning value(assertion_Failed) type abap_Bool,
252 |
253 | "! Ensure that text matches regular expression
254 | "!
255 | "! @parameter pattern | Regular Expression - see also TA ABAPHELP
256 | "! @parameter text | Text that is assumed to met the regular expression
257 | "! @parameter msg | Description
258 | "! @parameter level | Severity (TOLERABLE, +CRITICAL+, FATAL)
259 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
260 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
261 | assert_Text_Matches
262 | importing value(pattern) type csequence
263 | value(text) type csequence
264 | !msg type csequence optional
265 | !level type int1 default if_Aunit_Constants=>severity-medium
266 | !quit type int1 default if_Aunit_Constants=>quit-test
267 | returning value(assertion_Failed) type abap_Bool,
268 |
269 | "! Ensure that a constraint is met by data object
270 | "!
271 | "! @parameter act | Data object which should adhere to constraint EXP
272 | "! @parameter act_As_Text | Description for ACT that is used in alert message text
273 | "! @parameter exp | Constraint to which ACT needs to adhere
274 | "! @parameter msg | Description
275 | "! @parameter level | Severity (TOLERABLE, +CRITICAL+, FATAL)
276 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
277 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
278 | assert_That
279 | importing value(act) type data
280 | value(act_As_Text) type csequence optional
281 | !exp type ref to if_Constraint
282 | !msg type csequence optional
283 | !level type int1 default if_Aunit_Constants=>severity-medium
284 | !quit type int1 default if_Aunit_Constants=>quit-test
285 | returning value(assertion_Failed) type abap_Bool,
286 |
287 | "! Ensure that boolean equals ABAP_TRUE
288 | "!
289 | "! @parameter act | Actual value
290 | "! @parameter msg | Description
291 | "! @parameter level | Severity (TOLERABLE, +CRITICAL+, FATAL)
292 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
293 | "! @parameter assertion_Failed | Condition was not met (and QUIT = NO)
294 | assert_True
295 | importing value(act) type abap_Bool
296 | !msg type csequence optional
297 | !level type int1 default if_Aunit_Constants=>severity-medium
298 | !quit type int1 default if_Aunit_Constants=>quit-test
299 | returning value(assertion_Failed) type abap_Bool,
300 |
301 | "! Report unconditional assertion
302 | "!
303 | "! @parameter msg | Description
304 | "! @parameter level | Severity (TOLERABLE, +CRITICAL+, FATAL)
305 | "! @parameter quit | Alter control flow/ quit test (NO, +METHOD+, CLASS)
306 | "! @parameter detail | Further Description
307 | fail
308 | importing !msg type csequence optional
309 | !level type int1 default if_Aunit_Constants=>severity-medium
310 | !quit type int1 default if_Aunit_Constants=>quit-test
311 | !detail type csequence optional
312 | preferred parameter msg.
313 |
314 | ENDCLASS.
315 |
316 |
317 |
318 | CLASS zcl_assert IMPLEMENTATION.
319 |
320 |
321 | method abort.
322 | cl_abap_unit_assert=>abort(
323 | exporting
324 | msg = msg " Description
325 | detail = detail " Further description
326 | quit = quit " Alter control flow/ quit test (METHOD, >>>CLASS<<<)
327 | ).
328 | endmethod.
329 |
330 |
331 | method assert_bound.
332 | cl_abap_unit_assert=>assert_bound(
333 | exporting
334 | act = act " Reference variable to be checked
335 | msg = msg " Description
336 | level = level " Severity (TOLERABLE, CRITICAL, FATAL)
337 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
338 | receiving
339 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
340 | ).
341 | endmethod.
342 |
343 |
344 | method assert_char_cp.
345 | cl_abap_unit_assert=>assert_char_cp(
346 | exporting
347 | act = act " Text to match to EXP pattern
348 | exp = exp " Expected simple text pattern
349 | msg = msg " Description
350 | level = level " Severity (TOLERABLE, CRITICAL, FATAL)
351 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
352 | receiving
353 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
354 | ).
355 | endmethod.
356 |
357 |
358 | method assert_char_np.
359 | cl_abap_unit_assert=>assert_char_np(
360 | exporting
361 | act = act " Actual text which shall not adhere to EXP pattern
362 | exp = exp " Simple text pattern
363 | msg = msg " Description
364 | level = level " Severity (TOLERABLE, CRITICAL, FATAL)
365 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
366 | receiving
367 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
368 | ).
369 | endmethod.
370 |
371 |
372 | method assert_differs.
373 | cl_abap_unit_assert=>assert_differs(
374 | exporting
375 | act = act
376 | exp = exp
377 | tol = tol
378 | msg = msg
379 | level = level
380 | quit = quit
381 | receiving
382 | assertion_failed = assertion_failed
383 | ).
384 | endmethod.
385 |
386 |
387 | method assert_equals.
388 | cl_abap_unit_assert=>assert_equals(
389 | exporting
390 | act = act " Data object with current value
391 | exp = exp " Data object with expected type
392 | ignore_hash_sequence = ignore_hash_sequence " Ignore sequence in hash tables
393 | tol = tol " Tolerance Range (for directly passed floating numbers)
394 | msg = msg " Description
395 | level = level " Severity (TOLERABLE, CRITICAL, FATAL)
396 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
397 | receiving
398 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
399 | ).
400 | endmethod.
401 |
402 |
403 | method assert_equals_float.
404 | cl_abap_unit_assert=>assert_equals_float(
405 | exporting
406 | act = act " Data object with current value
407 | exp = exp " Data object with expected value
408 | rtol = rtol " Relative tolerance
409 | msg = msg " Description
410 | level = level " Severity (TOLERABLE, CRITICAL, FATAL)
411 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
412 | receiving
413 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
414 | ).
415 |
416 | endmethod.
417 |
418 |
419 | method assert_false.
420 | cl_abap_unit_assert=>assert_false(
421 | exporting
422 | act = act " Actual data object
423 | msg = msg " Description
424 | level = level " Severity (TOLERABLE, >CRITICAL<, FATAL)
425 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
426 | receiving
427 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
428 | ).
429 |
430 | endmethod.
431 |
432 |
433 | method assert_initial.
434 | cl_abap_unit_assert=>assert_initial(
435 | exporting
436 | act = act " Actual data object
437 | msg = msg " Description
438 | level = level " Severity (TOLERABLE, >CRITICAL<, FATAL)
439 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
440 | receiving
441 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
442 | ).
443 | endmethod.
444 |
445 |
446 | method assert_not_bound.
447 | cl_abap_unit_assert=>assert_not_bound(
448 | exporting
449 | act = act " Reference variable to be checked
450 | msg = msg " Description
451 | level = level " Severity (TOLERABLE, >CRITICAL<, FATAL)
452 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
453 | receiving
454 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
455 | ).
456 | endmethod.
457 |
458 |
459 | method assert_not_initial.
460 | cl_abap_unit_assert=>assert_not_initial(
461 | exporting
462 | act = act " Actual Data Object
463 | msg = msg " Message in Case of Error
464 | level = level " Severity (TOLERABLE, >CRITICAL<, FATAL)
465 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
466 | receiving
467 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
468 | ).
469 | endmethod.
470 |
471 |
472 | method assert_number_between.
473 | cl_abap_unit_assert=>assert_number_between(
474 | exporting
475 | lower = lower " Upper Boundary
476 | upper = upper " Lower Boundary
477 | number = number " Number exepected to LOWER <= NUMBER <= UPPER
478 | msg = msg " Description
479 | level = level " Alter control flow/ quit test (NO, >METHOD<, CLASS)
480 | quit = quit " Control flow in case of failed assertion
481 | receiving
482 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
483 | ).
484 | endmethod.
485 |
486 |
487 | method assert_subrc.
488 | cl_abap_unit_assert=>assert_subrc(
489 | exporting
490 | exp = exp " Expected return code, optional, if not zero
491 | act = act " Return code of ABAP statements
492 | msg = msg " Description
493 | level = level " Severity (TOLERABLE, >CRITICAL<, FATAL)
494 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
495 | symsg = symsg " System message
496 | receiving
497 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
498 | ).
499 | endmethod.
500 |
501 |
502 | method assert_table_contains.
503 | cl_abap_unit_assert=>assert_table_contains(
504 | exporting
505 | line = line " Data Object that is typed like line of TABLE
506 | table = table " Internal Table that shall contain LINE
507 | msg = msg " Description
508 | level = level " Severity (TOLERABLE, >CRITICAL<, FATAL)
509 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
510 | receiving
511 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
512 | ).
513 | endmethod.
514 |
515 |
516 | method assert_table_not_contains.
517 | cl_abap_unit_assert=>assert_table_not_contains(
518 | exporting
519 | line = line " Data Object that is typed like line of TABLE
520 | table = table " Internal Table that must not contain LINE
521 | msg = msg " Description
522 | level = level " Severity (TOLERABLE, >CRITICAL<, FATAL)
523 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
524 | receiving
525 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
526 | ).
527 | endmethod.
528 |
529 |
530 | method assert_text_matches.
531 | cl_abap_unit_assert=>assert_text_matches(
532 | exporting
533 | pattern = pattern " Regular Expression - see also TA ABAPHELP
534 | text = text " Text that is assumed to met the regular expression
535 | msg = msg " Description
536 | level = level " Severity (TOLERABLE, >CRITICAL<, FATAL)
537 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
538 | receiving
539 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
540 | ).
541 |
542 | endmethod.
543 |
544 |
545 | method assert_that.
546 | cl_abap_unit_assert=>assert_that(
547 | exporting
548 | act = act " Data Object which should adhere to constraint EXP
549 | exp = exp " Constraint to which ACT needs to adhere
550 | msg = msg " Description
551 | level = level " Severity (TOLERABLE, >CRITICAL<, FATAL)
552 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
553 | receiving
554 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
555 | ).
556 | endmethod.
557 |
558 |
559 | method assert_true.
560 | cl_abap_unit_assert=>assert_true(
561 | exporting
562 | act = act " Actual value
563 | msg = msg " Description
564 | level = level " Severity (TOLERABLE, >CRITICAL<, FATAL)
565 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
566 | receiving
567 | assertion_failed = assertion_failed " Condition was not met (and QUIT = NO)
568 | ).
569 | endmethod.
570 |
571 |
572 | method fail.
573 | cl_abap_unit_assert=>fail(
574 | exporting
575 | msg = msg " Description
576 | level = level " Severity (TOLERABLE, >CRITICAL<, FATAL)
577 | quit = quit " Alter control flow/ quit test (NO, >METHOD<, CLASS)
578 | detail = detail " Further Description
579 | ).
580 | endmethod.
581 | ENDCLASS.
582 |
--------------------------------------------------------------------------------
/src/zcl_assert.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_ASSERT
7 | E
8 | cl_abap_unit_assert proxy class for inheritance
9 | 05
10 | 1
11 | X
12 | X
13 | X
14 | 12
15 | 11
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/zcl_default_thread_factory.clas.abap:
--------------------------------------------------------------------------------
1 | "! Default implementation for thread factories
2 | "! The default thread factory creates normal thread objects. If no task name is provided, the factory will create a unique one. @TODO
3 | "! Currently the thread creates its own task name if none provided
4 | "!
5 | class zcl_default_thread_factory definition
6 | public
7 | final
8 | create public .
9 |
10 | public section.
11 |
12 | interfaces zif_thread_factory .
13 | protected section.
14 | private section.
15 | endclass.
16 |
17 |
18 |
19 | class zcl_default_thread_factory implementation.
20 |
21 | method zif_thread_factory~new_thread.
22 |
23 | ro_result = new zcl_thread(
24 | io_runnable = io_runnable
25 | io_callback = io_callback
26 | iv_taskname = iv_taskname
27 | ).
28 | endmethod.
29 |
30 | endclass.
31 |
--------------------------------------------------------------------------------
/src/zcl_default_thread_factory.clas.testclasses.abap:
--------------------------------------------------------------------------------
1 | class ltc_thread_factory definition final for testing
2 | duration short
3 | risk level harmless
4 | inheriting from zcl_assert.
5 |
6 | public section.
7 | interfaces zif_thread_callback.
8 |
9 | private section.
10 | data:
11 | o_cut type ref to zif_thread_factory,
12 | v_called_back type abap_bool.
13 | methods:
14 | setup,
15 | it_creates_threads for testing.
16 | endclass.
17 |
18 |
19 | class ltc_thread_factory implementation.
20 |
21 |
22 | method setup.
23 | clear v_called_back.
24 | o_cut = new ZCL_DEFAULT_THREAD_FACTORY( ).
25 | endmethod.
26 |
27 | method it_creates_threads.
28 |
29 | data(lo_thread) = o_cut->new_thread(
30 | io_runnable = new zcl_runnable_dummy( )
31 | io_callback = me
32 | iv_taskname = 'TestThread'
33 | ).
34 |
35 | lo_thread->start( ).
36 | lo_thread->join( ).
37 |
38 | assert_equals(
39 | exp = 'TestThread'
40 | act = lo_thread->get_taskname( )
41 | ).
42 | assert_true( v_called_back ).
43 |
44 | endmethod.
45 |
46 | method zif_thread_callback~on_result.
47 | v_called_Back = abap_true.
48 | endmethod.
49 |
50 | method zif_thread_callback~on_error.
51 |
52 | endmethod.
53 |
54 | endclass.
55 |
--------------------------------------------------------------------------------
/src/zcl_default_thread_factory.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_DEFAULT_THREAD_FACTORY
7 | E
8 | Default implementation for thread factories
9 | 1
10 | X
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/zcl_dummy_runnable_result.clas.abap:
--------------------------------------------------------------------------------
1 | class zcl_dummy_runnable_result definition
2 | public
3 | final
4 | create public .
5 |
6 | public section.
7 |
8 | interfaces if_serializable_object .
9 | interfaces zif_runnable_result .
10 | protected section.
11 | private section.
12 | endclass.
13 |
14 |
15 |
16 | class zcl_dummy_runnable_result implementation.
17 | endclass.
18 |
--------------------------------------------------------------------------------
/src/zcl_dummy_runnable_result.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_DUMMY_RUNNABLE_RESULT
7 | E
8 | A dummy result for threads with no result
9 | 1
10 | X
11 | X
12 | X
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/zcl_executors.clas.abap:
--------------------------------------------------------------------------------
1 | "! Class for Executor services factory methods.
2 | class zcl_executors definition
3 | public
4 | final
5 | create public .
6 |
7 | public section.
8 | class-methods:
9 | new_single_thread_executor
10 | importing
11 | io_callback type ref to zif_thread_callback optional
12 | io_thread_factory type ref to zif_thread_factory optional
13 | returning value(ro_result) type ref to zif_executor_service,
14 | new_fixed_thread_pool
15 | importing
16 | iv_threads type i
17 | io_callback type ref to zif_thread_callback optional
18 | io_thread_factory type ref to zif_thread_factory optional
19 | returning value(ro_result) type ref to zif_executor_service.
20 | protected section.
21 | private section.
22 | endclass.
23 |
24 |
25 |
26 | class zcl_executors implementation.
27 |
28 | method new_fixed_thread_pool.
29 | ro_result = new zcl_thread_pool_executor(
30 | iv_pool_size = iv_threads
31 | io_callback = io_callback
32 | io_thread_factory = io_thread_factory
33 | ).
34 | endmethod.
35 |
36 | method new_single_thread_executor.
37 | ro_result = new zcl_thread_pool_executor(
38 | iv_pool_size = 1
39 | io_callback = io_callback
40 | io_thread_factory = io_thread_factory
41 | ).
42 | endmethod.
43 |
44 | endclass.
45 |
--------------------------------------------------------------------------------
/src/zcl_executors.clas.testclasses.abap:
--------------------------------------------------------------------------------
1 | class ltc_executors definition final for testing
2 | duration short
3 | risk level harmless
4 | inheriting from zcl_assert.
5 |
6 | public section.
7 | interfaces zif_thread_callback.
8 | private section.
9 | data:
10 | o_cut type ref to ZCL_EXECUTORS,
11 | v_collected type i.
12 | methods:
13 | setup,
14 | it_returns_single_thread for testing,
15 | it_returns_fixed_pool for testing,
16 | it_pass_callback_and_factory for testing.
17 | endclass.
18 |
19 |
20 | class ltc_executors implementation.
21 |
22 |
23 | method setup.
24 | clear v_collected.
25 | endmethod.
26 |
27 | method it_returns_single_thread.
28 |
29 | assert_bound( zcl_executors=>new_single_thread_executor( ) ).
30 |
31 | endmethod.
32 |
33 | method it_returns_fixed_pool.
34 |
35 | assert_bound( zcl_executors=>new_fixed_thread_pool( 2 ) ).
36 |
37 | endmethod.
38 |
39 | method it_pass_callback_and_factory.
40 |
41 | data(lo_deactivated) = new zcl_deactivated_thread_factory( ).
42 | lo_deactivated->set_thread_call_result(
43 | iv_thread_num = 1
44 | io_result = new zcl_dummy_runnable_result( )
45 | ).
46 | lo_deactivated->set_thread_call_result(
47 | iv_thread_num = 2
48 | io_result = new zcl_dummy_runnable_result( )
49 | ).
50 |
51 | data(lo_executor) = zcl_executors=>new_fixed_thread_pool(
52 | iv_threads = 2
53 | io_callback = me
54 | io_thread_factory = lo_deactivated ).
55 |
56 |
57 | lo_executor->submit( new zcl_runnable_dummy( 1 ) ).
58 | lo_executor->submit( new zcl_runnable_dummy( 1 ) ).
59 | lo_executor->await_termination( ).
60 |
61 | assert_equals(
62 | exp = 2
63 | act = lines( lo_deactivated->get_created_threads( ) )
64 | msg = 'Should have passed factory'
65 | ).
66 |
67 | assert_equals(
68 | exp = 2
69 | act = v_collected
70 | msg = 'Should have passed call back'
71 | ).
72 |
73 |
74 |
75 | endmethod.
76 |
77 |
78 | method zif_thread_callback~on_error.
79 | v_collected = v_collected + 1.
80 | endmethod.
81 |
82 | method zif_thread_callback~on_result.
83 | v_collected = v_collected + 1.
84 | endmethod.
85 |
86 | endclass.
87 |
--------------------------------------------------------------------------------
/src/zcl_executors.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_EXECUTORS
7 | E
8 | Class for Executor services factory methods
9 | 1
10 | X
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/zcl_thread.clas.abap:
--------------------------------------------------------------------------------
1 | "! Thread for ABAP
2 | "! You can use threads in ABAP by either:
3 | "!
4 | "! - extending the ZCL_THREAD class and redefining the method zif_runnable~run
5 | "! class zcl_prime_numbers inheriting from zcl_thread ...
6 | "! data(lo_thread) = new zcl_prime_numbers( 1 )->start( ).
7 | "!
8 | "! - implementing the zif_runnable interface and pass it to a thread.
9 | "!
10 | "! class zcl_prime_numbers...interfaces zif_runnable
11 | "! data(lo_thread) = new zcl_thread( new zcl_prime_numbers( 1 ) )->start( )
12 | "!
13 | "!
14 | "!
15 | "!
16 | "! Threads will always execute using a dialog work process.
17 | "!
18 | class zcl_thread definition
19 | public
20 | create public .
21 |
22 | public section.
23 | interfaces zif_runnable.
24 | class-methods:
25 | "! Waits for all asynchronous work to finish for the calling program.
26 | join_all
27 | importing
28 | iv_timeout type zethread_wait_time optional.
29 | methods:
30 | "! Creates a new Thread
31 | "! @parameter io_runnable | The runnable to execute asynchronously.
32 | "! @parameter io_callback | A callback for when the thread has finished.
33 | "! @parameter iv_taskname | An optional unique task name
34 | constructor
35 | importing
36 | io_runnable type ref to zif_runnable optional
37 | io_callback type ref to zif_thread_callback optional
38 | iv_taskname type char32 optional
39 | preferred parameter io_runnable,
40 |
41 | "! Returns its given name or the uniquely generated one, in case none provided.
42 | "! @parameter rv_result | Task name
43 | get_taskname
44 | returning value(rv_result) type char32,
45 | "! Starts the thread. In case there are no available work process, it waits
46 | start,
47 | "! Returns if a thread is currently running or not
48 | "! @parameter rv_result | result
49 | is_running
50 | returning value(rv_result) type abap_bool,
51 | "! Returns the thread result. In case the thread is still running it raises zcx_still_running
52 | "!
53 | "! @parameter ro_result |
54 | get_result
55 | returning value(ro_result) type ref to zif_runnable_result,
56 |
57 | "! Returns error occurred during a thread run.
58 | "! @parameter ro_result | The error
59 | get_error
60 | returning value(ro_result) type ref to cx_root,
61 | "! Waits for the thread to finish
62 | join
63 | importing
64 | iv_timeout type zethread_wait_time optional,
65 | "! Used internally to receive thread result.
66 | "! @parameter p_task | task id
67 | on_end
68 | importing
69 | p_task type clike.
70 | protected section.
71 | private section.
72 | constants gc_wait_period_interval type zethread_wait_time value '0.1'.
73 | data o_runnable type ref to zif_runnable.
74 | data v_is_running type abap_bool.
75 | data o_result type ref to zif_runnable_result.
76 | data o_callback type ref to zif_thread_callback.
77 | data v_taskname type char32.
78 | data o_error type ref to cx_root.
79 | class-methods:
80 | check_timeout_reached
81 | importing
82 | iv_timeout type zethread_wait_time
83 | iv_start type datldref-typint
84 | iv_now type datldref-typint.
85 | methods:
86 | is_runnable
87 | returning
88 | value(rv_is_runnable) type xsdboolean,
89 | serialize_runnable
90 | returning
91 | value(rv_serialized) type string,
92 | wait_for_free_dialog,
93 | new_task_id
94 | returning
95 | value(rv_task_id) type char32,
96 | start_asynchronously
97 | importing
98 | iv_serialized type string,
99 | callback_on_result
100 | importing
101 | iv_task type clike
102 | iv_result type string,
103 | callback_on_error
104 | importing
105 | iv_task type clike
106 | iv_error type string,
107 | check_still_running.
108 | endclass.
109 |
110 |
111 |
112 | class zcl_thread implementation.
113 |
114 | method join_all.
115 |
116 | get run time field data(lv_start).
117 | do.
118 | "not sure how wait works without up to seconds
119 | "fearing CPU time eating by this, I'm waiting relatively long for the next check
120 | wait for asynchronous tasks
121 | until abap_true = abap_false
122 | up to gc_wait_period_interval seconds.
123 | get run time field data(lv_now).
124 |
125 | "4 = The logical expression log_exp is false.
126 | "Also the current program does not contain any asynchronous function calls with callback routines,
127 | "and no receiver is registered for AMC messages or APC messages for the use of the additions
128 | "MESSAGING CHANNELS or PUSH CHANNELS.
129 | if sy-subrc = 4.
130 | return.
131 | endif.
132 |
133 | check_timeout_reached(
134 | iv_timeout = iv_timeout
135 | iv_start = lv_start
136 | iv_now = lv_now ).
137 | enddo.
138 |
139 | endmethod.
140 |
141 | method constructor.
142 | v_taskname = cond #(
143 | when iv_taskname is initial
144 | then new_task_id( )
145 | else iv_taskname
146 | ).
147 | o_runnable = cond #(
148 | when io_runnable is bound
149 | then io_runnable
150 | else me ).
151 |
152 | o_callback = io_callback.
153 | if not is_runnable( ).
154 | raise exception type zcx_not_a_runnable.
155 | endif.
156 | endmethod.
157 |
158 | method zif_runnable~run.
159 | "implement on extending thread classes
160 | "or create default thread with a runnable
161 | if me = o_runnable.
162 | raise exception type zcx_not_a_runnable.
163 | endif.
164 | o_runnable->run( ).
165 | endmethod.
166 |
167 | method get_taskname.
168 | rv_result = v_taskname.
169 | endmethod.
170 |
171 | method start.
172 |
173 | v_is_running = abap_true.
174 |
175 | data(lv_serialized) = serialize_runnable( ).
176 |
177 | wait_for_free_dialog( ).
178 |
179 | start_asynchronously( lv_serialized ).
180 |
181 | endmethod.
182 |
183 | method is_running.
184 | rv_result = v_is_running.
185 | endmethod.
186 |
187 | method get_result.
188 | check_still_running( ).
189 | ro_result = o_result.
190 | endmethod.
191 |
192 | method get_error.
193 | check_still_running( ).
194 | ro_result = o_error.
195 | endmethod.
196 |
197 | method join.
198 |
199 | get run time field data(lv_start).
200 | do.
201 |
202 | wait for asynchronous tasks
203 | until is_running( ) = abap_false
204 | up to gc_wait_period_interval seconds.
205 | get run time field data(lv_now).
206 |
207 | if sy-subrc = 0.
208 | return.
209 | endif.
210 |
211 | check_timeout_reached(
212 | iv_timeout = iv_timeout
213 | iv_start = lv_start
214 | iv_now = lv_now ).
215 |
216 | enddo.
217 |
218 | endmethod.
219 |
220 | method on_end.
221 |
222 |
223 | data(lv_result) = value string( ).
224 | data(lv_error) = value string( ).
225 | receive results from function 'ZTHREAD_START'
226 | importing
227 | ev_result = lv_result
228 | ev_error = lv_error.
229 |
230 | v_is_running = abap_false.
231 |
232 | callback_on_result(
233 | iv_task = p_task
234 | iv_result = lv_result ).
235 |
236 | callback_on_error(
237 | iv_task = p_task
238 | iv_error = lv_error ).
239 |
240 | endmethod.
241 |
242 | method is_runnable.
243 |
244 | check o_runnable is bound.
245 |
246 | data(lo_describer) = cast cl_abap_objectdescr( cl_abap_typedescr=>describe_by_object_ref( o_runnable ) ).
247 | data(lv_name) = lo_describer->absolute_name.
248 | rv_is_runnable = xsdbool(
249 | o_runnable is instance of zif_runnable
250 | "root thread class has empty run and cannot be started
251 | and lv_name <> '\CLASS=ZCL_THREAD'
252 | ).
253 |
254 | endmethod.
255 |
256 | method serialize_runnable.
257 |
258 | call transformation id
259 | source runnable = o_runnable
260 | result xml rv_serialized.
261 |
262 | endmethod.
263 |
264 |
265 | method wait_for_free_dialog.
266 | "hardcoded 4 on purpose. Less than that and systems get weird.
267 | data num_free_dia_wps type i.
268 | data num_wps type i.
269 | data attempt_num type i.
270 | constants opcode_wp_get_info type x value 25.
271 |
272 |
273 |
274 | call function 'SPBT_INITIALIZE'
275 | importing
276 | free_pbt_wps = num_free_dia_wps " PBT resources that are currently free
277 | exceptions
278 | others = 7.
279 |
280 | if sy-subrc = 0 and num_free_dia_wps >= 4.
281 | return.
282 | endif.
283 |
284 | while num_free_dia_wps < 4.
285 | call function 'SPBT_GET_CURR_RESOURCE_INFO'
286 | importing
287 | free_pbt_wps = num_free_dia_wps
288 | exceptions
289 | others = 3.
290 |
291 | if sy-subrc <> 0.
292 | num_free_dia_wps = 0.
293 | endif.
294 |
295 | "can lead to starvation if server usage is too high for too long
296 | wait up to gc_wait_period_interval seconds.
297 |
298 | attempt_num = attempt_num + 1.
299 |
300 | if attempt_num > 600. "wait for a minute at maximum (600 x 0.1s)
301 | raise exception type zcx_thread_start_fail.
302 | endif.
303 |
304 | endwhile.
305 |
306 | endmethod.
307 |
308 | method start_asynchronously.
309 |
310 | call function 'ZTHREAD_START'
311 | starting new task v_taskname
312 | destination in group DEFAULT
313 | "destination 'NONE'
314 | calling on_end on end of task
315 | exporting
316 | iv_runnable = iv_serialized
317 | exceptions
318 | error_message = 1
319 | system_failure = 2
320 | communication_failure = 3
321 | resource_failure = 4.
322 |
323 | if sy-subrc > 0.
324 | raise exception type zcx_thread_start_fail.
325 | endif.
326 |
327 | endmethod.
328 |
329 | method new_task_id.
330 |
331 | try.
332 | rv_task_id = cl_system_uuid=>create_uuid_x16_static( ).
333 | catch cx_uuid_error.
334 | "not unique task ids has slightly different behavior.
335 | "most of the cases leading to errors in execution!
336 | rv_task_id = 'NOT_UNIQUE'.
337 | endtry.
338 |
339 | endmethod.
340 |
341 | method callback_on_result.
342 |
343 | check iv_result is not initial.
344 |
345 |
346 | call transformation id
347 | source xml iv_result
348 | result result = o_result.
349 |
350 | check o_callback is bound.
351 | o_callback->on_result(
352 | iv_taskname = iv_task
353 | io_result = o_result
354 | ).
355 |
356 |
357 | endmethod.
358 |
359 |
360 | method callback_on_error.
361 |
362 | check iv_error is not initial.
363 |
364 |
365 | call transformation id
366 | source xml iv_error
367 | result error = o_error.
368 |
369 |
370 | check o_callback is bound.
371 | o_callback->on_error(
372 | iv_taskname = iv_task
373 | io_error = o_error
374 | ).
375 |
376 |
377 | endmethod.
378 |
379 |
380 | method check_still_running.
381 |
382 | if is_running( ).
383 | raise exception type zcx_still_running.
384 | endif.
385 |
386 | endmethod.
387 |
388 |
389 | method check_timeout_reached.
390 | check iv_timeout is not initial
391 | and iv_timeout <> 0
392 | and iv_timeout > 0.
393 |
394 | data(lv_waited_in_tenths) = conv zethread_wait_time( ( ( iv_now - iv_start ) / 1000000 ) ).
395 | if lv_waited_in_tenths >= iv_timeout.
396 | raise exception type zcx_wait_timeout
397 | exporting
398 | waited = lv_waited_in_tenths.
399 | endif.
400 |
401 | endmethod.
402 |
403 | endclass.
404 |
--------------------------------------------------------------------------------
/src/zcl_thread.clas.testclasses.abap:
--------------------------------------------------------------------------------
1 | "! Tests Thread functionality. Coverage is not correct as on_end/handle_if_success/handle_if_error methods
2 | "! are executed as callback from the FM. The AUNIT framework doesn't detect that these methods were called
3 | "! during the test execution.
4 | class ltc_thread definition final for testing
5 | duration short
6 | risk level harmless
7 | inheriting from zcl_assert.
8 |
9 | public section.
10 | interfaces zif_thread_callback.
11 | private section.
12 | data:
13 | o_cut type ref to zcl_thread,
14 | o_runnable type ref to zcl_runnable_dummy,
15 | v_collected type i,
16 | v_last_collected type char32,
17 | v_last_failed type char32.
18 | methods:
19 | setup,
20 | it_has_name for testing,
21 | it_raises_not_a_runnable for testing,
22 | it_aruns_runnables_returns_res for testing,
23 | it_raises_still_running for testing,
24 | it_limits_threads for testing,
25 | it_allows_for_callbacks for testing,
26 | it_callsback_with_error for testing,
27 | it_returns_error for testing,
28 | it_joins_started_thread for testing,
29 | it_joins_all_threads for testing,
30 | it_times_out_join for testing.
31 | endclass.
32 |
33 |
34 | class ltc_thread implementation.
35 |
36 |
37 | method setup.
38 | clear:
39 | v_collected,
40 | v_last_collected,
41 | v_last_failed.
42 |
43 | o_runnable = new zcl_runnable_dummy( ).
44 | o_cut = new zcl_thread( o_runnable ).
45 | endmethod.
46 |
47 | method it_has_name.
48 |
49 | "given name
50 | o_cut = new zcl_thread(
51 | iv_taskname = 'MyTestThread'
52 | io_runnable = o_runnable
53 | io_callback = me ).
54 |
55 | assert_equals(
56 | exp = 'MyTestThread'
57 | act = o_cut->get_taskname( )
58 | msg = 'Should have stored task name'
59 | ).
60 |
61 | o_cut->start( ).
62 | o_cut->join( ).
63 |
64 | assert_equals(
65 | exp = 'MyTestThread'
66 | act = v_last_collected
67 | msg = 'Should inform task name on callbacks'
68 | ).
69 |
70 | "no name given, unique id is generated
71 | o_cut = new zcl_thread( o_runnable ).
72 | assert_not_initial( o_cut->get_taskname( ) ).
73 |
74 | endmethod.
75 |
76 | method it_raises_not_a_runnable.
77 |
78 | try.
79 | o_cut = new zcl_thread( value #( ) ).
80 | catch zcx_not_a_runnable into data(lo_failure).
81 | ##NO_HANDLER
82 | endtry.
83 |
84 | assert_bound( lo_failure ).
85 |
86 | endmethod.
87 |
88 | method it_aruns_runnables_returns_res.
89 | "runs runnable and return results
90 | "ZCL_RUNNABLE_DUMMY is also implementing zif_runnable_result
91 | o_cut->start( ).
92 | assert_true( o_cut->is_running( ) ).
93 |
94 | o_cut->join( ).
95 | assert_false( o_cut->is_running( ) ).
96 |
97 | data(lo_result) = cast zcl_runnable_dummy( o_cut->get_result( ) ).
98 | assert_true( lo_result->was_run_called( ) ).
99 |
100 |
101 | endmethod.
102 |
103 |
104 | method it_allows_for_callbacks.
105 | "test class is implementing the zif_thread_callback interface
106 | "counting each time a thread finishes
107 | do 10 times.
108 |
109 | o_cut = new zcl_thread(
110 | io_runnable = o_runnable
111 | io_callback = me ).
112 | o_cut->start( ).
113 |
114 |
115 | enddo.
116 |
117 | zcl_thread=>join_all( ).
118 |
119 | assert_equals(
120 | exp = 10
121 | act = v_collected
122 | msg = 'Join should wait for all to complete'
123 | ).
124 |
125 |
126 | endmethod.
127 |
128 | method it_callsback_with_error.
129 | "the test class is implementing the callback interface
130 |
131 | o_runnable->raise_on_run( ).
132 | o_cut = new zcl_thread(
133 | io_runnable = o_runnable
134 | io_callback = me
135 | iv_taskname = 'MyTest' ).
136 | o_cut->start( ).
137 | o_cut->join( ).
138 |
139 |
140 | assert_equals(
141 | exp = 'MyTest'
142 | act = v_last_failed
143 | msg = 'Error should provide task name'
144 | ).
145 |
146 |
147 | endmethod.
148 |
149 |
150 | method it_returns_error.
151 |
152 | o_runnable->raise_on_run( ).
153 |
154 | o_cut = new zcl_thread(
155 | io_runnable = o_runnable
156 | iv_taskname = 'MyTest' ).
157 |
158 | o_cut->start( ).
159 | o_cut->join( ).
160 |
161 |
162 | assert_bound(
163 | act = o_cut->get_error( )
164 | msg = 'Should return error.'
165 | ).
166 |
167 |
168 |
169 | endmethod.
170 |
171 |
172 | method it_raises_still_running.
173 |
174 |
175 | try.
176 | o_cut = new zcl_thread( o_runnable ).
177 | o_cut->start( ).
178 | o_cut->get_result( ).
179 | catch zcx_still_running into data(lo_failure).
180 | ##NO_HANDLER
181 | endtry.
182 |
183 | assert_bound( lo_failure ).
184 |
185 | endmethod.
186 |
187 | method it_limits_threads.
188 | "should not raise any run time error, as it will wait
189 | "for workers to be available
190 | "tested for max mp dia = 25
191 | do 50 times.
192 |
193 | o_cut = new zcl_thread( new zcl_runnable_dummy( '0.1' ) ).
194 | o_cut->start( ).
195 |
196 | enddo.
197 |
198 | endmethod.
199 |
200 | method it_joins_started_thread.
201 | "join allows for joining back threads to the main execution
202 | "here we are setting the runnable to wait for a second
203 | "the join call will wait for the thread to finish
204 |
205 | o_cut = new zcl_thread( new zcl_runnable_dummy( '1' ) ).
206 |
207 | o_cut->start( ).
208 | assert_true( o_cut->is_running( ) ).
209 |
210 | o_cut->join( ).
211 | assert_false( o_cut->is_running( ) ).
212 |
213 | endmethod.
214 |
215 | method it_joins_all_threads.
216 | "Join all allows for the wait of all executed threads
217 | do 10 times.
218 |
219 | o_cut = new zcl_thread(
220 | io_runnable = o_runnable
221 | io_callback = me ).
222 | o_cut->start( ).
223 |
224 |
225 | enddo.
226 |
227 | zcl_thread=>join_all( ).
228 |
229 | assert_equals(
230 | exp = 10
231 | act = v_collected
232 | msg = 'Join should wait for all to complete'
233 | ).
234 |
235 |
236 |
237 | endmethod.
238 |
239 | method it_times_out_join.
240 |
241 |
242 | "to avoid flaky test, still flaky depending on system usage
243 | data lo_failure type ref to cx_root.
244 | do 5 times.
245 | "should raise
246 | try.
247 | clear lo_failure.
248 | o_runnable = new zcl_runnable_dummy( '0.2' ).
249 | o_cut = new zcl_thread( o_runnable ).
250 | o_cut->start( ).
251 | o_cut->join( '0.1' ).
252 |
253 |
254 | catch zcx_wait_timeout into lo_failure.
255 | assert_bound( lo_failure ).
256 | endtry.
257 | assert_bound( lo_failure ).
258 |
259 | "should not raise
260 | clear lo_failure.
261 | o_runnable = new zcl_runnable_dummy( '0.1' ).
262 | o_cut = new zcl_thread( o_runnable ).
263 | o_cut->start( ).
264 | o_cut->join( '0.2' ).
265 | "as proof of completion
266 | o_cut->get_result( ).
267 |
268 |
269 |
270 | enddo.
271 |
272 | endmethod.
273 |
274 | method zif_thread_callback~on_result.
275 |
276 | v_collected = v_collected + 1.
277 | v_last_collected = iv_taskname.
278 | assert_bound( io_result ).
279 |
280 | endmethod.
281 |
282 | method zif_thread_callback~on_error.
283 |
284 | v_last_failed = iv_taskname.
285 | assert_bound( io_error ).
286 |
287 | endmethod.
288 |
289 | endclass.
290 |
--------------------------------------------------------------------------------
/src/zcl_thread.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_THREAD
7 | E
8 | Thread c
9 | 1
10 | X
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 | CONSTRUCTOR
18 | E
19 | Creates a new Thread
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/zcl_thread_future.clas.abap:
--------------------------------------------------------------------------------
1 | class zcl_thread_future definition
2 | public
3 | final
4 | create public .
5 |
6 | public section.
7 |
8 | interfaces zif_future .
9 | methods:
10 | constructor
11 | importing
12 | io_thread type ref to zcl_thread.
13 | protected section.
14 | private section.
15 | data o_thread type ref to zcl_thread.
16 | methods check_for_execution_error.
17 | endclass.
18 |
19 |
20 |
21 | class zcl_thread_future implementation.
22 |
23 | method constructor.
24 | o_thread = io_thread.
25 | endmethod.
26 |
27 | method zif_future~get.
28 | o_thread->join( iv_timeout ).
29 | check_for_execution_error( ).
30 | ro_result = o_thread->get_result( ).
31 | endmethod.
32 |
33 | method zif_future~is_done.
34 | "@todo: implement has_started to complement
35 | rv_result = xsdbool( o_thread->is_running( ) = abap_false ).
36 | endmethod.
37 |
38 |
39 | method check_for_execution_error.
40 |
41 | data(lo_error) = o_thread->get_error( ).
42 | if lo_error is bound.
43 | raise exception type zcx_thread_execution
44 | exporting
45 | previous = lo_error.
46 | endif.
47 |
48 | endmethod.
49 |
50 | endclass.
51 |
--------------------------------------------------------------------------------
/src/zcl_thread_future.clas.testclasses.abap:
--------------------------------------------------------------------------------
1 | class ltc_thread_future definition final for testing
2 | duration short
3 | risk level harmless
4 | inheriting from zcl_assert.
5 |
6 | private section.
7 | data:
8 | o_cut type ref to zif_future,
9 | o_dummy_runnable type ref to zcl_runnable_dummy,
10 | o_thread type ref to zcl_thread.
11 | methods:
12 | setup,
13 | it_returns_is_done for testing,
14 | it_waits_and_returns_result for testing,
15 | it_raises_error for testing,
16 | it_passes_timeout for testing.
17 | endclass.
18 |
19 |
20 | class ltc_thread_future implementation.
21 |
22 |
23 | method setup.
24 | o_dummy_runnable = new zcl_runnable_dummy( '1' ).
25 | o_thread = new zcl_thread( o_dummy_runnable ).
26 | o_cut = new zcl_thread_future( o_thread ).
27 | endmethod.
28 |
29 | method it_returns_is_done.
30 |
31 |
32 | o_thread->start( ).
33 | assert_false( o_cut->is_done( ) ).
34 | o_thread->join( ).
35 | assert_true( o_cut->is_done( ) ).
36 |
37 |
38 | endmethod.
39 |
40 | method it_waits_and_returns_result.
41 |
42 | o_thread->start( ).
43 | data(lo_result) = o_cut->get( ).
44 | assert_bound( lo_result ).
45 | assert_true( cast zcl_runnable_dummy( lo_result )->was_run_called( ) ).
46 |
47 |
48 | endmethod.
49 |
50 | method it_raises_error.
51 |
52 | o_dummy_runnable->raise_on_run( ).
53 | o_thread->start( ).
54 | try.
55 | o_cut->get( ).
56 | catch zcx_thread_execution into data(lo_failure).
57 | assert_bound( lo_failure ).
58 | endtry.
59 | assert_bound( lo_failure ).
60 |
61 |
62 | endmethod.
63 |
64 | method it_passes_timeout.
65 |
66 | o_dummy_runnable = new zcl_runnable_dummy( '0.2' ).
67 | o_thread->start( ).
68 | try.
69 | o_cut->get( '0.1' ).
70 | catch zcx_wait_timeout into data(lo_failure).
71 | assert_bound( lo_failure ).
72 | endtry.
73 | assert_bound( lo_failure ).
74 |
75 |
76 | endmethod.
77 |
78 |
79 | endclass.
80 |
--------------------------------------------------------------------------------
/src/zcl_thread_future.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_THREAD_FUTURE
7 | E
8 | A thread future
9 | 1
10 | X
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/zcl_thread_pool_executor.clas.abap:
--------------------------------------------------------------------------------
1 | "! Thread pool executor...
2 | "! Limits concurrent thread execution to a predefined thread pool size.
3 | "! Current implementation blocks main thread!
4 | "!
5 | "!
6 | class zcl_thread_pool_executor definition
7 | public
8 | create public.
9 |
10 | public section.
11 | constants gc_pool_wait_interval type decfloat16 value '0.1'.
12 |
13 | interfaces: zif_executor_service.
14 | methods:
15 | constructor
16 | importing
17 | iv_pool_size type i
18 | io_callback type ref to zif_thread_callback optional
19 | io_thread_factory type ref to zif_thread_factory optional,
20 | get_default_thread_factory
21 | returning value(ro_result) type ref to zif_thread_factory.
22 | protected section.
23 | private section.
24 |
25 | data v_pool_size type i.
26 | data t_threads_to_start type zif_executor_service=>tty_threads.
27 | data v_shutdown type abap_bool.
28 | data o_thread_factory type ref to zif_thread_factory.
29 | data o_thread_pool type ref to lcl_thread_pool.
30 | data t_futures type zif_executor_service=>tty_futures.
31 | methods get_next
32 | returning
33 | value(ro_to_run) type ref to zcl_thread.
34 | methods has_next
35 | returning
36 | value(rv_has_next) type xsdboolean.
37 | methods is_pool_full
38 | returning
39 | value(rv_pool_is_full) type xsdboolean.
40 | methods start_thread_in_pool
41 | importing
42 | io_thread type ref to zcl_thread
43 | returning value(ro_thread) type ref to zcl_thread.
44 | methods thread_factory_lookup
45 | importing
46 | io_thread_factory type ref to zif_thread_factory.
47 | methods start_thread_queue.
48 | methods check_is_shutdown.
49 | methods new_thread
50 | importing
51 | io_runnable type ref to zif_runnable
52 | returning
53 | value(ro_new_thread) type ref to zcl_thread.
54 | methods create_threads_return_futures
55 | importing
56 | it_runnables type zif_executor_service=>tty_runnables
57 | returning value(rt_futures) type zif_executor_service=>tty_futures.
58 | endclass.
59 |
60 |
61 |
62 | class zcl_thread_pool_executor implementation.
63 |
64 |
65 | method constructor.
66 | v_pool_size = iv_pool_size.
67 | thread_factory_lookup( io_thread_factory ).
68 | o_thread_pool = new lcl_thread_pool( io_callback ).
69 |
70 | endmethod.
71 |
72 | method zif_executor_service~await_termination.
73 | zcl_thread=>join_all( iv_timeout ).
74 | endmethod.
75 |
76 | method zif_executor_service~shutdown.
77 | v_shutdown = abap_true.
78 | endmethod.
79 |
80 | method zif_executor_service~shutdown_now.
81 | v_shutdown = abap_true.
82 | rt_not_ran = t_threads_to_start.
83 | endmethod.
84 |
85 |
86 | method zif_executor_service~submit.
87 | check_is_shutdown( ).
88 | data(lt_futures) = create_threads_return_futures( value #( ( io_runnable ) ) ).
89 | start_thread_queue( ).
90 | ro_future = value #( lt_futures[ 1 ] optional ).
91 | endmethod.
92 |
93 |
94 | method zif_executor_service~invoke_all.
95 | check_is_shutdown( ).
96 | rt_futures = create_threads_return_futures( it_runnables ).
97 | start_thread_queue( ).
98 | endmethod.
99 |
100 | method get_default_thread_factory.
101 | ro_result = new zcl_default_thread_factory( ).
102 | endmethod.
103 |
104 | method get_next.
105 |
106 | ro_to_run = t_threads_to_start[ 1 ].
107 | delete t_threads_to_start index 1.
108 |
109 | endmethod.
110 |
111 |
112 | method has_next.
113 |
114 | rv_has_next = xsdbool( lines( t_threads_to_start ) > 0 ).
115 |
116 | endmethod.
117 |
118 |
119 | method is_pool_full.
120 |
121 | rv_pool_is_full = xsdbool( o_thread_pool->get_size( ) >= v_pool_size ).
122 |
123 | endmethod.
124 |
125 |
126 | method thread_factory_lookup.
127 |
128 | o_thread_factory = cond #(
129 | when io_thread_factory is bound
130 | then io_thread_factory
131 | else get_default_thread_factory( ) ).
132 |
133 | endmethod.
134 |
135 | method start_thread_queue.
136 | "todo: return futures
137 | "also: this is the piece of code that should run inside a memory area class
138 | "with a thread->callback->thread loop to itself so we don't block the execution of the main thread
139 | while has_next( ).
140 |
141 | while is_pool_full( ).
142 | wait up to gc_pool_wait_interval seconds.
143 | endwhile.
144 |
145 | while not is_pool_full( )
146 | and has_next( ).
147 |
148 | data(lo_next) = get_next( ).
149 | append
150 | new zcl_thread_future(
151 | start_thread_in_pool( lo_next )
152 | )
153 | to t_futures.
154 |
155 | endwhile.
156 |
157 | endwhile.
158 |
159 | endmethod.
160 |
161 | method start_thread_in_pool.
162 |
163 | o_thread_pool->start( io_thread ).
164 |
165 | endmethod.
166 |
167 |
168 | method check_is_shutdown.
169 |
170 | if v_shutdown = abap_true.
171 | raise exception type zcx_rejected_execution.
172 | endif.
173 |
174 | endmethod.
175 |
176 | method create_threads_return_futures.
177 |
178 | loop at it_runnables into data(lo_runnable).
179 | data(lo_thread) = new_thread( lo_runnable ).
180 | append new zcl_thread_future( lo_thread ) to rt_futures.
181 | append lo_thread to t_threads_to_start.
182 |
183 | endloop.
184 |
185 | endmethod.
186 |
187 | method new_thread.
188 |
189 | ro_new_thread = o_thread_factory->new_thread(
190 | io_runnable = io_runnable
191 | io_callback = o_thread_pool
192 | ).
193 |
194 | endmethod.
195 |
196 |
197 | endclass.
198 |
--------------------------------------------------------------------------------
/src/zcl_thread_pool_executor.clas.locals_def.abap:
--------------------------------------------------------------------------------
1 | class lcl_thread_pool definition create public.
2 |
3 | public section.
4 | interfaces zif_thread_callback.
5 | methods:
6 | constructor
7 | importing
8 | io_callback type ref to zif_thread_callback optional,
9 | start
10 | importing
11 | io_thread type ref to zcl_thread,
12 | get_size
13 | returning value(rv_result) type i.
14 | protected section.
15 | private section.
16 | data v_threads_running type i.
17 | data o_callback type ref to zif_thread_callback.
18 | data o_thread_factory type ref to zif_thread_factory.
19 |
20 | endclass.
21 |
--------------------------------------------------------------------------------
/src/zcl_thread_pool_executor.clas.locals_imp.abap:
--------------------------------------------------------------------------------
1 | class lcl_thread_pool implementation.
2 |
3 | method constructor.
4 | v_threads_running = 0.
5 | o_callback = io_callback.
6 | endmethod.
7 |
8 | method get_size.
9 | rv_result = v_threads_running.
10 | endmethod.
11 |
12 | method start.
13 | v_threads_running = v_threads_running + 1.
14 | io_thread->start( ).
15 | endmethod.
16 |
17 | method zif_thread_callback~on_error.
18 | v_threads_running = v_threads_running - 1.
19 | check o_callback is bound.
20 | o_callback->on_error(
21 | io_error = io_error
22 | iv_taskname = iv_taskname ).
23 | endmethod.
24 |
25 | method zif_thread_callback~on_result.
26 | v_threads_running = v_threads_running - 1.
27 | check o_callback is bound.
28 | o_callback->on_result(
29 | io_result = io_result
30 | iv_taskname = iv_taskname ).
31 |
32 | endmethod.
33 |
34 | endclass.
35 |
--------------------------------------------------------------------------------
/src/zcl_thread_pool_executor.clas.testclasses.abap:
--------------------------------------------------------------------------------
1 | class ltc_thread_pool_executor definition final for testing
2 | duration short
3 | risk level harmless
4 | inheriting from zcl_assert.
5 |
6 | public section.
7 | interfaces zif_thread_callback.
8 | private section.
9 | data:
10 | o_cut type ref to zif_executor_service,
11 | v_calledback type i.
12 | methods:
13 | setup,
14 | it_submits_runnables for testing,
15 | it_invokes_all_and_waits for testing,
16 | it_shutsdown for testing,
17 | it_returns_futures for testing.
18 | endclass.
19 |
20 |
21 | class ltc_thread_pool_executor implementation.
22 |
23 |
24 | method setup.
25 | v_calledback = 0.
26 | o_cut = new zcl_thread_pool_executor(
27 | iv_pool_size = 3
28 | io_callback = me ).
29 | endmethod.
30 |
31 | method zif_thread_callback~on_result.
32 | v_calledback = v_calledback + 1.
33 | endmethod.
34 |
35 | method zif_thread_callback~on_error.
36 | v_calledback = v_calledback + 1.
37 | endmethod.
38 |
39 |
40 | method it_submits_runnables.
41 |
42 | data(lo_dummy) = new zcl_runnable_dummy( ).
43 | o_cut->submit( lo_dummy ).
44 | wait up to '0.2' seconds.
45 |
46 | assert_equals(
47 | exp = 1
48 | act = v_calledback
49 | msg = 'Should have executed the runnable in a new Thread'
50 | ).
51 |
52 |
53 | endmethod.
54 |
55 | method it_invokes_all_and_waits.
56 |
57 | data(lt_runnables) = value zif_executor_service=>tty_runnables( ).
58 | do 30 times.
59 | append new zcl_runnable_dummy( ) to lt_runnables.
60 | enddo.
61 |
62 | o_cut->invoke_all( lt_runnables ).
63 | o_cut->invoke_all( lt_runnables ).
64 |
65 | o_cut->await_termination( ).
66 |
67 | assert_equals(
68 | exp = 60
69 | act = v_calledback
70 | ).
71 |
72 | endmethod.
73 |
74 | method it_shutsdown.
75 |
76 | data(lo_dummy) = new zcl_runnable_dummy( ).
77 | o_cut->submit( lo_dummy ).
78 | wait up to '0.2' seconds.
79 |
80 | assert_equals(
81 | exp = 1
82 | act = v_calledback
83 | msg = 'Should have executed the runnable in a new Thread'
84 | ).
85 |
86 |
87 | o_cut->shutdown( ).
88 |
89 | try.
90 | o_cut->submit( lo_dummy ).
91 | catch zcx_rejected_execution into data(lo_submit_failure).
92 | assert_bound( lo_submit_failure ).
93 | endtry.
94 | assert_bound( lo_submit_failure ).
95 |
96 |
97 | try.
98 | o_cut->invoke_all( value #( ( lo_dummy ) ) ).
99 | catch zcx_rejected_execution into data(lo_invoke_failure).
100 | assert_bound( lo_invoke_failure ).
101 | endtry.
102 | assert_bound( lo_invoke_failure ).
103 |
104 |
105 | endmethod.
106 |
107 | method it_returns_futures.
108 |
109 | data(lo_dummy) = new zcl_runnable_dummy( ).
110 |
111 | "submit
112 | data(lo_future) = o_cut->submit( lo_dummy ).
113 | assert_bound( lo_future ).
114 | assert_true( cast zcl_runnable_dummy( lo_future->get( ) )->was_run_called( ) ).
115 |
116 | "invoke_all
117 | data(lt_futures) = o_cut->invoke_all( value #( ( lo_dummy ) ( lo_dummy ) ) ).
118 | loop at lt_futures into lo_future.
119 | assert_true( cast zcl_runnable_dummy( lo_future->get( ) )->was_run_called( ) ).
120 | endloop.
121 |
122 |
123 | endmethod.
124 |
125 |
126 | endclass.
127 |
--------------------------------------------------------------------------------
/src/zcl_thread_pool_executor.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_THREAD_POOL_EXECUTOR
7 | E
8 | A thread pool executor
9 | 1
10 | X
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/zcx_not_a_runnable.clas.abap:
--------------------------------------------------------------------------------
1 | class zcx_not_a_runnable definition
2 | public
3 | inheriting from cx_no_check
4 | final
5 | create public .
6 |
7 | public section.
8 |
9 | interfaces if_t100_dyn_msg .
10 | interfaces if_t100_message .
11 | constants:
12 | begin of zcx_message,
13 | msgid type symsgid value 'ZTHREAD',
14 | msgno type symsgno value '001',
15 | attr1 type scx_attrname value '',
16 | attr2 type scx_attrname value '',
17 | attr3 type scx_attrname value '',
18 | attr4 type scx_attrname value '',
19 | end of zcx_message .
20 |
21 | methods constructor
22 | importing
23 | !textid like if_t100_message=>t100key optional
24 | !previous like previous optional .
25 | protected section.
26 | private section.
27 | endclass.
28 |
29 |
30 |
31 | class zcx_not_a_runnable implementation.
32 |
33 |
34 | method constructor ##ADT_SUPPRESS_GENERATION.
35 | call method super->constructor
36 | exporting
37 | previous = previous.
38 | clear me->textid.
39 | if textid is initial.
40 | if_t100_message~t100key = zcx_message.
41 | else.
42 | if_t100_message~t100key = textid.
43 | endif.
44 | endmethod.
45 | endclass.
46 |
--------------------------------------------------------------------------------
/src/zcx_not_a_runnable.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCX_NOT_A_RUNNABLE
7 | E
8 | Not a runnable exception
9 | 40
10 | 1
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 | CONSTRUCTOR
18 | E
19 | CONSTRUCTOR
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/zcx_rejected_execution.clas.abap:
--------------------------------------------------------------------------------
1 | class zcx_rejected_execution definition
2 | public
3 | inheriting from cx_no_check
4 | final
5 | create public .
6 |
7 | public section.
8 |
9 | interfaces if_t100_dyn_msg .
10 | interfaces if_t100_message .
11 | constants:
12 | begin of zcx_message,
13 | msgid type symsgid value 'ZTHREAD',
14 | msgno type symsgno value '005',
15 | attr1 type scx_attrname value '',
16 | attr2 type scx_attrname value '',
17 | attr3 type scx_attrname value '',
18 | attr4 type scx_attrname value '',
19 | end of zcx_message .
20 | methods constructor
21 | importing
22 | !textid like if_t100_message=>t100key optional
23 | !previous like previous optional .
24 | protected section.
25 | private section.
26 | endclass.
27 |
28 |
29 |
30 | class zcx_rejected_execution implementation.
31 |
32 |
33 | method constructor ##ADT_SUPPRESS_GENERATION.
34 | call method super->constructor
35 | exporting
36 | previous = previous.
37 | clear me->textid.
38 | if textid is initial.
39 | if_t100_message~t100key = zcx_message.
40 | else.
41 | if_t100_message~t100key = textid.
42 | endif.
43 | endmethod.
44 | endclass.
45 |
--------------------------------------------------------------------------------
/src/zcx_rejected_execution.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCX_REJECTED_EXECUTION
7 | E
8 | Thrown by an executor when a task cannot be accepted.
9 | 40
10 | 1
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 | CONSTRUCTOR
18 | E
19 | CONSTRUCTOR
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/zcx_still_running.clas.abap:
--------------------------------------------------------------------------------
1 | class zcx_still_running definition
2 | public
3 | inheriting from cx_no_check
4 | final
5 | create public .
6 |
7 | public section.
8 |
9 | interfaces if_t100_dyn_msg .
10 | interfaces if_t100_message .
11 | constants:
12 | begin of zcx_message,
13 | msgid type symsgid value 'ZTHREAD',
14 | msgno type symsgno value '002',
15 | attr1 type scx_attrname value '',
16 | attr2 type scx_attrname value '',
17 | attr3 type scx_attrname value '',
18 | attr4 type scx_attrname value '',
19 | end of zcx_message .
20 |
21 | methods constructor
22 | importing
23 | !textid like if_t100_message=>t100key optional
24 | !previous like previous optional .
25 | protected section.
26 | private section.
27 | endclass.
28 |
29 |
30 |
31 | class zcx_still_running implementation.
32 |
33 |
34 | method constructor ##ADT_SUPPRESS_GENERATION.
35 | call method super->constructor
36 | exporting
37 | previous = previous.
38 | clear me->textid.
39 | if textid is initial.
40 | if_t100_message~t100key = zcx_message.
41 | else.
42 | if_t100_message~t100key = textid.
43 | endif.
44 | endmethod.
45 | endclass.
46 |
--------------------------------------------------------------------------------
/src/zcx_still_running.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCX_STILL_RUNNING
7 | E
8 | Not a runnable exception
9 | 40
10 | 1
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 | CONSTRUCTOR
18 | E
19 | CONSTRUCTOR
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/zcx_thread_execution.clas.abap:
--------------------------------------------------------------------------------
1 | class zcx_thread_execution definition
2 | public
3 | inheriting from cx_no_check
4 | final
5 | create public .
6 |
7 | public section.
8 |
9 | interfaces if_t100_dyn_msg .
10 | interfaces if_t100_message .
11 | constants:
12 | begin of zcx_message,
13 | msgid type symsgid value 'ZTHREAD',
14 | msgno type symsgno value '006',
15 | attr1 type scx_attrname value '',
16 | attr2 type scx_attrname value '',
17 | attr3 type scx_attrname value '',
18 | attr4 type scx_attrname value '',
19 | end of zcx_message .
20 | methods constructor
21 | importing
22 | !textid like if_t100_message=>t100key optional
23 | !previous like previous optional .
24 | protected section.
25 | private section.
26 | endclass.
27 |
28 |
29 |
30 | class zcx_thread_execution implementation.
31 |
32 |
33 | method constructor ##ADT_SUPPRESS_GENERATION.
34 | call method super->constructor
35 | exporting
36 | previous = previous.
37 | clear me->textid.
38 | if textid is initial.
39 | if_t100_message~t100key = zcx_message.
40 | else.
41 | if_t100_message~t100key = textid.
42 | endif.
43 | endmethod.
44 | endclass.
45 |
--------------------------------------------------------------------------------
/src/zcx_thread_execution.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCX_THREAD_EXECUTION
7 | E
8 | Thrown by an executor when a task cannot be accepted.
9 | 40
10 | 1
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 | CONSTRUCTOR
18 | E
19 | CONSTRUCTOR
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/zcx_thread_start_fail.clas.abap:
--------------------------------------------------------------------------------
1 | class zcx_thread_start_fail definition
2 | public
3 | inheriting from cx_no_check
4 | final
5 | create public .
6 |
7 | public section.
8 |
9 | interfaces if_t100_dyn_msg .
10 | interfaces if_t100_message .
11 | constants:
12 | begin of zcx_message,
13 | msgid type symsgid value 'ZTHREAD',
14 | msgno type symsgno value '003',
15 | attr1 type scx_attrname value '',
16 | attr2 type scx_attrname value '',
17 | attr3 type scx_attrname value '',
18 | attr4 type scx_attrname value '',
19 | end of zcx_message .
20 |
21 | methods constructor
22 | importing
23 | !textid like if_t100_message=>t100key optional
24 | !previous like previous optional .
25 | protected section.
26 | private section.
27 | endclass.
28 |
29 |
30 |
31 | class zcx_thread_start_fail implementation.
32 |
33 |
34 | method constructor ##ADT_SUPPRESS_GENERATION.
35 | call method super->constructor
36 | exporting
37 | previous = previous.
38 | clear me->textid.
39 | if textid is initial.
40 | if_t100_message~t100key = zcx_message.
41 | else.
42 | if_t100_message~t100key = textid.
43 | endif.
44 | endmethod.
45 | endclass.
46 |
--------------------------------------------------------------------------------
/src/zcx_thread_start_fail.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCX_THREAD_START_FAIL
7 | E
8 | Not a runnable exception
9 | 40
10 | 1
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 | CONSTRUCTOR
18 | E
19 | CONSTRUCTOR
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/zcx_wait_timeout.clas.abap:
--------------------------------------------------------------------------------
1 | class zcx_wait_timeout definition
2 | public
3 | inheriting from cx_no_check
4 | final
5 | create public .
6 |
7 | public section.
8 |
9 | interfaces if_t100_dyn_msg .
10 | interfaces if_t100_message .
11 | data waited type zethread_wait_time.
12 | constants:
13 | begin of zcx_message,
14 | msgid type symsgid value 'ZTHREAD',
15 | msgno type symsgno value '007',
16 | attr1 type scx_attrname value 'WAITED',
17 | attr2 type scx_attrname value '',
18 | attr3 type scx_attrname value '',
19 | attr4 type scx_attrname value '',
20 | end of zcx_message .
21 | methods constructor
22 | importing
23 | !textid like if_t100_message=>t100key optional
24 | !previous like previous optional
25 | !waited type zethread_wait_time optional.
26 | protected section.
27 | private section.
28 | endclass.
29 |
30 |
31 |
32 | class zcx_wait_timeout implementation.
33 |
34 |
35 | method constructor ##ADT_SUPPRESS_GENERATION.
36 | call method super->constructor
37 | exporting
38 | previous = previous.
39 | clear me->textid.
40 | if textid is initial.
41 | if_t100_message~t100key = zcx_message.
42 | else.
43 | if_t100_message~t100key = textid.
44 | endif.
45 | me->waited = waited.
46 | endmethod.
47 | endclass.
48 |
--------------------------------------------------------------------------------
/src/zcx_wait_timeout.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCX_WAIT_TIMEOUT
7 | E
8 | Thrown by an executor when a task cannot be accepted.
9 | 40
10 | 1
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 | CONSTRUCTOR
18 | E
19 | CONSTRUCTOR
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/zdthread_wait_time.doma.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZDTHREAD_WAIT_TIME
7 | E
8 | DEC
9 | 000005
10 | 000007
11 | 000001
12 | Time in sec with tenth precision
13 | E
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/zethread_wait_time.dtel.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZETHREAD_WAIT_TIME
7 | E
8 | ZDTHREAD_WAIT_TIME
9 | 55
10 | 10
11 | 20
12 | 40
13 | Time in seconds with tenth precision
14 | E
15 | D
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/zif_executor_service.intf.abap:
--------------------------------------------------------------------------------
1 | "! A service executor for Threads
2 | "! A service executor provides the methods to manage termination and tracking of Threads results.
3 | "! Please refer to class Executor for factory methods.
4 | "!
5 | interface zif_executor_service
6 | public .
7 |
8 | types:
9 | tty_threads type standard table of ref to zcl_thread with non-unique default key,
10 | tty_runnables type standard table of ref to zif_runnable with non-unique default key,
11 | tty_futures type standard table of ref to zif_future with non-unique default key.
12 | methods:
13 | "! Submits all runnables for execution according to executor rules and return a list of futures for each started thread.
14 | "! If a callback is provided, it is called when the runnable finishes its execution
15 | "! The thread result can be retrieved with the get method of a future. This awaits for the Thread to complete.
16 | "! @parameter it_runnables | Runnables to execute
17 | "! @parameter rt_futures | Futures
18 | invoke_All
19 | importing
20 | it_runnables type tty_runnables
21 | returning value(rt_futures) type tty_futures,
22 | "! Submits the runnable to run asynchronously in a new thread.
23 | "! If a callback is provided, it is called when the runnable finishes its execution
24 | "! The thread result can be retrieved with the get method of a future. This awaits for the Thread to complete.
25 | "! @parameter io_runnable | The runnable to submit
26 | submit
27 | importing
28 | io_runnable type ref to zif_runnable
29 | returning value(ro_future) type ref to zif_future,
30 | "! Awaits for all submitted tasks to finish their execution.
31 | "! Similarly to a thread.join( ), this can be used to wait join back forked worked.
32 | "! @parameter iv_timeout |
Timeout for the wait
33 | await_termination
34 | importing
35 | iv_timeout type zethread_wait_time optional,
36 | "! Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.
37 | "! Does not wait previously submitted tasks to end. Use await_termination for that.
38 | "!
39 | shutdown,
40 | "! Attempts to stop all actively executing tasks, halts the processing of waiting tasks,
41 | "! and returns a list of the tasks that were awaiting execution.
42 | "! Does not wait previously submitted tasks to end. Use await_termination for that.
43 | "! @parameter rt_not_ran |
A list of all the runnables that were not executed
44 | shutdown_Now
45 | returning value(rt_not_ran) type tty_runnables.
46 |
47 | endinterface.
48 |
--------------------------------------------------------------------------------
/src/zif_executor_service.intf.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZIF_EXECUTOR_SERVICE
7 | E
8 | A service executor for Threads
9 | 2
10 | 1
11 | X
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/zif_future.intf.abap:
--------------------------------------------------------------------------------
1 | "! A future representing thre result of asynchronous computation
2 | interface zif_future
3 | public .
4 |
5 | methods:
6 | "! Returns true in case future is resolved.
7 | "!
8 | "! @parameter rv_result | is done
9 | is_done returning value(rv_result) type abap_bool,
10 | "! Get the future result.
11 | "! This will make the relevant thread to wait for its completion.
12 | "! Raises zcx_execution_error in case thread ends in error.
13 | "!
14 | "! @parameter iv_timeout | Maximum time for wait for resolution.
15 | "! @parameter ro_result | Thread result
16 | get
17 | importing
18 | iv_timeout type zethread_wait_time optional
19 | returning value(ro_result) type ref to zif_runnable_result.
20 |
21 |
22 |
23 | endinterface.
24 |
--------------------------------------------------------------------------------
/src/zif_future.intf.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZIF_FUTURE
7 | E
8 | A thread future interface
9 | 2
10 | 1
11 | X
12 |
13 |
14 |
15 | IS_DONE
16 | E
17 | Returns true in case future is resolved.
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/zif_runnable.intf.abap:
--------------------------------------------------------------------------------
1 | "! Runnable interface
2 | "! A class implementing this interface can be used by a thread to execute the work defined in run.
3 | "!
4 | interface ZIF_RUNNABLE
5 | public .
6 |
7 | interfaces if_serializable_object.
8 | "! Run
9 | "! This method is called by a thread asynchronously to execute work.
10 | "! The returned result must implement the specified interface, this result can be returned by the Thread once its finished.
11 | "! The thread also passes this result to the callback function, if defined in its creation.
12 | "!
13 | "! @parameter ro_result | result of runnable
14 | methods run
15 | returning value(ro_result) type ref to zif_runnable_result.
16 |
17 | endinterface.
18 |
--------------------------------------------------------------------------------
/src/zif_runnable.intf.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZIF_RUNNABLE
7 | E
8 | Runnable
9 | 2
10 | 1
11 | X
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/zif_runnable_result.intf.abap:
--------------------------------------------------------------------------------
1 | "! Interface for runnable result.
2 | "! Results from runnables must implement this interface. The result can be taken from the thread after it is finished or in
3 | "! the callback routine, if defined in the Thread creation.
4 | "! Whoever fetches this result must now its concrete type so it can access its data.
5 | "! for example:
6 | "! class zcl_a_result...interfaces zif_runnable_result
7 | "! methods:
8 | "! set_some_result importing iv_a_result type something.
9 | "! get_some_result returning value(rv_result) type something.
10 | "! ...
11 | "! on the runnable-run:
12 | "! data(lo_result) = new zcl_a_result( ).
13 | "! lo_result->set_some_result( ... )
14 | "! ro_result = lo_result.
15 | "! ...
16 | "! on your callback:
17 | "! data(lo_result) = cast zcl_a_result( io_result ).
18 | "! data(lv_real_result) = lo_result->get_some_result( )
19 | "! ...
20 | "! after a thread finishes:
21 | "! lo_thread->join( ).
22 | "! data(lo_result) = cast zcl_a_result( lo_thread->get_result( ) ).
23 | "! data(lv_real_result) = lo_result->get_some_result( )
24 | "!
25 | "!
26 | interface ZIF_RUNNABLE_RESULT
27 | public .
28 |
29 | interfaces if_serializable_object.
30 |
31 | endinterface.
32 |
--------------------------------------------------------------------------------
/src/zif_runnable_result.intf.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZIF_RUNNABLE_RESULT
7 | E
8 | result
9 | 2
10 | 1
11 | X
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/zif_thread_callback.intf.abap:
--------------------------------------------------------------------------------
1 | "! Callback interface
2 | "! A callback can be provided to threads to handle its results or errors after it finishes
3 | "! its processing.
4 | "!
5 | interface zif_thread_callback
6 | public .
7 |
8 | methods:
9 | "! Callback method
10 | "! This method is called in case of a successful execution
11 | "! @parameter iv_taskname | Task name if provided during Thread creation or its unique UID
12 | "! @parameter io_result | Result of the Thread processing
13 | on_result
14 | importing
15 | iv_taskname type char32
16 | io_result type ref to zif_runnable_result,
17 | "! On error method
18 | "! This method is called in case there was an error in the thread processing.
19 | "! For it to work properly, only no check exceptions must be raised in the Thread.
20 | "! @parameter iv_taskname | Task name if provided during thread creation or its unique UID
21 | "! @parameter io_error | Exception raised by the thread execution
22 | on_error
23 | importing
24 | iv_taskname type char32
25 | io_error type ref to cx_root.
26 |
27 | endinterface.
28 |
--------------------------------------------------------------------------------
/src/zif_thread_callback.intf.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZIF_THREAD_CALLBACK
7 | E
8 | results collector
9 | 2
10 | 1
11 | X
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/zif_thread_factory.intf.abap:
--------------------------------------------------------------------------------
1 | "! An interface for thread factories
2 | "! This interface is used by thread factories to default values or return your own extensions of a thread.
3 | "! It is mostly used for dependency injection in tests.
4 | "!
5 | interface zif_thread_factory
6 | public .
7 | "! Returns a new thread
8 | "!
9 | "! @parameter io_runnable | Runnable for the thread
10 | "! @parameter io_callback | Callback for the thread
11 | "! @parameter iv_taskname | Task name for the thread
12 | "! @parameter ro_result | The returned thread.
13 | methods
14 | new_thread
15 | importing
16 | io_runnable type ref to zif_runnable
17 | io_callback type ref to zif_thread_callback optional
18 | iv_taskname type char32 optional
19 | returning value(ro_result) type ref to zcl_thread.
20 |
21 | endinterface.
22 |
--------------------------------------------------------------------------------
/src/zif_thread_factory.intf.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZIF_THREAD_FACTORY
7 | E
8 | An interface for thread factories
9 | 2
10 | 1
11 | X
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/zthread.msag.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZTHREAD
7 | E
8 | Threads message class
9 |
10 |
11 |
12 | E
13 | ZTHREAD
14 | 001
15 | Not a runnable
16 |
17 |
18 | E
19 | ZTHREAD
20 | 002
21 | Thread still running
22 |
23 |
24 | E
25 | ZTHREAD
26 | 003
27 | Unable to start a thread.
28 |
29 |
30 | E
31 | ZTHREAD
32 | 004
33 | &1&2&3&4
34 |
35 |
36 | E
37 | ZTHREAD
38 | 005
39 | Executer is shut(ing) down.
40 |
41 |
42 | E
43 | ZTHREAD
44 | 006
45 | An error ocurred during thread execution.
46 |
47 |
48 | E
49 | ZTHREAD
50 | 007
51 | Wait timeout (&1s) reached.
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/src/zthread_bg.fugr.lzthread_bgtop.abap:
--------------------------------------------------------------------------------
1 | FUNCTION-POOL ZTHREAD_BG. "MESSAGE-ID ..
2 |
3 | * INCLUDE LZTHREAD_BGD... " Local class definition
4 |
--------------------------------------------------------------------------------
/src/zthread_bg.fugr.lzthread_bgtop.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LZTHREAD_BGTOP
7 | S
8 | D$
9 | I
10 | S
11 | X
12 | D$S
13 | X
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/zthread_bg.fugr.saplzthread_bg.abap:
--------------------------------------------------------------------------------
1 | *******************************************************************
2 | * System-defined Include-files. *
3 | *******************************************************************
4 | INCLUDE LZTHREAD_BGTOP. " Global Declarations
5 | INCLUDE LZTHREAD_BGUXX. " Function Modules
6 |
7 | *******************************************************************
8 | * User-defined Include-files (if necessary). *
9 | *******************************************************************
10 | * INCLUDE LZTHREAD_BGF... " Subroutines
11 | * INCLUDE LZTHREAD_BGO... " PBO-Modules
12 | * INCLUDE LZTHREAD_BGI... " PAI-Modules
13 | * INCLUDE LZTHREAD_BGE... " Events
14 | * INCLUDE LZTHREAD_BGP... " Local class implement.
15 | * INCLUDE LZTHREAD_BGT99. " ABAP Unit tests
16 |
--------------------------------------------------------------------------------
/src/zthread_bg.fugr.saplzthread_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | SAPLZTHREAD_BG
7 | S
8 | D$
9 | F
10 | S
11 | E
12 | X
13 | D$S
14 | X
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/zthread_bg.fugr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Background FMs
6 |
7 | LZTHREAD_BGTOP
8 | SAPLZTHREAD_BG
9 |
10 |
11 | -
12 | ZTHREAD_START
13 | R
14 | Bg thread start
15 |
16 |
17 | IV_RUNNABLE
18 | STRING
19 |
20 |
21 |
22 |
23 | EV_RESULT
24 | STRING
25 |
26 |
27 | EV_ERROR
28 | STRING
29 |
30 |
31 |
32 |
33 | IV_RUNNABLE
34 | P
35 |
36 |
37 | EV_RESULT
38 | P
39 |
40 |
41 | EV_ERROR
42 | P
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/zthread_bg.fugr.zthread_start.abap:
--------------------------------------------------------------------------------
1 | FUNCTION ZTHREAD_START.
2 | *"----------------------------------------------------------------------
3 | *"*"Local Interface:
4 | *" IMPORTING
5 | *" VALUE(IV_RUNNABLE) TYPE STRING
6 | *" EXPORTING
7 | *" VALUE(EV_RESULT) TYPE STRING
8 | *" VALUE(EV_ERROR) TYPE STRING
9 | *"----------------------------------------------------------------------
10 |
11 | data lo_runnable type ref to zif_runnable.
12 |
13 | call transformation id
14 | source xml iv_runnable
15 | result runnable = lo_runnable.
16 |
17 | try.
18 |
19 | data(lo_result) = lo_runnable->run( ).
20 |
21 | catch cx_root into data(lo_failure).
22 | "all exceptions are serializable by default
23 | call transformation id
24 | source error = lo_failure
25 | result xml ev_error.
26 | return.
27 |
28 | endtry.
29 |
30 | if lo_result is not bound.
31 | lo_result = new zcl_dummy_runnable_result( ).
32 | endif.
33 |
34 | call transformation id
35 | source result = lo_result
36 | result xml ev_result.
37 |
38 |
39 | endfunction.
40 |
--------------------------------------------------------------------------------