├── .abapgit.xml ├── ABAPDoc ├── abapdoc │ └── zthread │ │ ├── classes │ │ ├── zcl_default_thread_factory.html │ │ ├── zcl_dummy_runnable_result.html │ │ ├── zcl_executors.html │ │ ├── zcl_thread.html │ │ ├── zcl_thread_future.html │ │ ├── zcl_thread_pool_executor.html │ │ ├── zcx_not_a_runnable.html │ │ ├── zcx_rejected_execution.html │ │ ├── zcx_still_running.html │ │ ├── zcx_thread_execution.html │ │ ├── zcx_thread_start_fail.html │ │ └── zcx_wait_timeout.html │ │ ├── interfaces │ │ ├── zif_executor_service.html │ │ ├── zif_future.html │ │ ├── zif_runnable.html │ │ ├── zif_runnable_result.html │ │ ├── zif_thread_callback.html │ │ └── zif_thread_factory.html │ │ ├── zthread_example │ │ └── classes │ │ │ ├── ztc_thread_example.html │ │ │ └── ztc_thread_pool_example.html │ │ └── zthread_tests │ │ └── classes │ │ ├── zcl_deactivated_thread.html │ │ ├── zcl_deactivated_thread_factory.html │ │ └── zcl_runnable_dummy.html ├── htmldesign │ └── stylesheet.css └── readme.txt ├── LICENSE ├── README.md ├── package.json └── src ├── example ├── package.devc.xml ├── ztc_thread_example.clas.abap ├── ztc_thread_example.clas.locals_imp.abap ├── ztc_thread_example.clas.xml ├── ztc_thread_pool_example.clas.abap ├── ztc_thread_pool_example.clas.locals_imp.abap └── ztc_thread_pool_example.clas.xml ├── package.devc.xml ├── tests ├── package.devc.xml ├── zcl_deactivated_thread.clas.abap ├── zcl_deactivated_thread.clas.xml ├── zcl_deactivated_thread_factory.clas.abap ├── zcl_deactivated_thread_factory.clas.xml ├── zcl_runnable_dummy.clas.abap └── zcl_runnable_dummy.clas.xml ├── zcl_assert.clas.abap ├── zcl_assert.clas.xml ├── zcl_default_thread_factory.clas.abap ├── zcl_default_thread_factory.clas.testclasses.abap ├── zcl_default_thread_factory.clas.xml ├── zcl_dummy_runnable_result.clas.abap ├── zcl_dummy_runnable_result.clas.xml ├── zcl_executors.clas.abap ├── zcl_executors.clas.testclasses.abap ├── zcl_executors.clas.xml ├── zcl_thread.clas.abap ├── zcl_thread.clas.testclasses.abap ├── zcl_thread.clas.xml ├── zcl_thread_future.clas.abap ├── zcl_thread_future.clas.testclasses.abap ├── zcl_thread_future.clas.xml ├── zcl_thread_pool_executor.clas.abap ├── zcl_thread_pool_executor.clas.locals_def.abap ├── zcl_thread_pool_executor.clas.locals_imp.abap ├── zcl_thread_pool_executor.clas.testclasses.abap ├── zcl_thread_pool_executor.clas.xml ├── zcx_not_a_runnable.clas.abap ├── zcx_not_a_runnable.clas.xml ├── zcx_rejected_execution.clas.abap ├── zcx_rejected_execution.clas.xml ├── zcx_still_running.clas.abap ├── zcx_still_running.clas.xml ├── zcx_thread_execution.clas.abap ├── zcx_thread_execution.clas.xml ├── zcx_thread_start_fail.clas.abap ├── zcx_thread_start_fail.clas.xml ├── zcx_wait_timeout.clas.abap ├── zcx_wait_timeout.clas.xml ├── zdthread_wait_time.doma.xml ├── zethread_wait_time.dtel.xml ├── zif_executor_service.intf.abap ├── zif_executor_service.intf.xml ├── zif_future.intf.abap ├── zif_future.intf.xml ├── zif_runnable.intf.abap ├── zif_runnable.intf.xml ├── zif_runnable_result.intf.abap ├── zif_runnable_result.intf.xml ├── zif_thread_callback.intf.abap ├── zif_thread_callback.intf.xml ├── zif_thread_factory.intf.abap ├── zif_thread_factory.intf.xml ├── zthread.msag.xml ├── zthread_bg.fugr.lzthread_bgtop.abap ├── zthread_bg.fugr.lzthread_bgtop.xml ├── zthread_bg.fugr.saplzthread_bg.abap ├── zthread_bg.fugr.saplzthread_bg.xml ├── zthread_bg.fugr.xml └── zthread_bg.fugr.zthread_start.abap /.abapgit.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | E 6 | /src/ 7 | PREFIX 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcl_default_thread_factory.html: -------------------------------------------------------------------------------- 1 | Class ZCL_DEFAULT_THREAD_FACTORY

Class ZCL_DEFAULT_THREAD_FACTORY

public final create public

Documentation

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 |

Interfaces

zif_thread_factory
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcl_dummy_runnable_result.html: -------------------------------------------------------------------------------- 1 | Class ZCL_DUMMY_RUNNABLE_RESULT

Class ZCL_DUMMY_RUNNABLE_RESULT

public final create public

Documentation

A dummy result for threads with no result

Interfaces

if_serializable_object
zif_runnable_result
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcl_executors.html: -------------------------------------------------------------------------------- 1 | Class ZCL_EXECUTORS

Class ZCL_EXECUTORS

public final create public

Documentation

Class for Executor services factory methods.

Methods

Visibility and LevelNameDocumentation
public static
new_fixed_thread_pool
importingiv_threadstype I
io_callbacktype ref to ZIF_THREAD_CALLBACK optional
io_thread_factorytype ref to ZIF_THREAD_FACTORY optional
returningvalue(ro_result)type ref to ZIF_EXECUTOR_SERVICE
 
public static
new_single_thread_executor
importingio_callbacktype ref to ZIF_THREAD_CALLBACK optional
io_thread_factorytype ref to ZIF_THREAD_FACTORY optional
returningvalue(ro_result)type ref to ZIF_EXECUTOR_SERVICE
 
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcl_thread.html: -------------------------------------------------------------------------------- 1 | Class ZCL_THREAD

Class ZCL_THREAD

public create public

Documentation

Thread for ABAP


2 | You can use threads in ABAP by either:
3 | 15 |
16 | Threads will always execute using a dialog work process. 17 |

Interfaces

if_serializable_object all methods abstract
zif_runnable

Methods

Visibility and LevelNameDocumentation
public static
join_all
importingiv_timeouttype ZETHREAD_WAIT_TIME optional

Waits for all asynchronous work to finish for the calling program.

public instance
constructor
importingio_runnabletype ref to ZIF_RUNNABLE optional
io_callbacktype ref to ZIF_THREAD_CALLBACK optional
iv_tasknametype CHAR32 optional

Creates a new Thread

18 |
Parameters
io_runnable

The runnable to execute asynchronously.

19 |
io_callback

A callback for when the thread has finished.

20 |
iv_taskname

An optional unique task name

public instance
get_error
returningvalue(ro_result)type ref to CX_ROOT

Returns error occurred during a thread run.

21 |
Parameters
ro_result

The error

public instance
get_result
returningvalue(ro_result)type ref to ZIF_RUNNABLE_RESULT

Returns the thread result. In case the thread is still running it raises zcx_still_running 22 |

23 |
Parameters
ro_result

public instance
get_taskname
returningvalue(rv_result)type CHAR32

Returns its given name or the uniquely generated one, in case none provided.

24 |
Parameters
rv_result

Task name

public instance
is_running
returningvalue(rv_result)type ABAP_BOOL

Returns if a thread is currently running or not

25 |
Parameters
rv_result

result

public instance
join
importingiv_timeouttype ZETHREAD_WAIT_TIME optional

Waits for the thread to finish

public instance
on_end
importingp_tasktype CLIKE

Used internally to receive thread result.

26 |
Parameters
p_task

task id

public instancestart

Starts the thread. In case there are no available work process, it waits

-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcl_thread_future.html: -------------------------------------------------------------------------------- 1 | Class ZCL_THREAD_FUTURE

Class ZCL_THREAD_FUTURE

public final create public

Documentation

A thread future

Interfaces

zif_future

Methods

Visibility and LevelNameDocumentation
public instance
constructor
importingio_threadtype ref to ZCL_THREAD
 
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcl_thread_pool_executor.html: -------------------------------------------------------------------------------- 1 | Class ZCL_THREAD_POOL_EXECUTOR

Class ZCL_THREAD_POOL_EXECUTOR

public create public

Documentation

Thread pool executor...
2 | Limits concurrent thread execution to a predefined thread pool size.
3 | Current implementation blocks main thread!
4 | 5 |

Interfaces

zif_executor_service

Constants

Visibility and LevelNameDocumentation
public staticgc_pool_wait_interval type DECFLOAT16 value '0.1' 

Methods

Visibility and LevelNameDocumentation
public instance
constructor
importingiv_pool_sizetype I
io_callbacktype ref to ZIF_THREAD_CALLBACK optional
io_thread_factorytype ref to ZIF_THREAD_FACTORY optional
 
public instance
get_default_thread_factory
returningvalue(ro_result)type ref to ZIF_THREAD_FACTORY
 
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcx_not_a_runnable.html: -------------------------------------------------------------------------------- 1 | Class ZCX_NOT_A_RUNNABLE

Class ZCX_NOT_A_RUNNABLE

public inheriting from CX_NO_CHECK final create public

Documentation

Not a runnable exception

Interfaces

if_t100_dyn_msg
if_t100_message

Constants

Visibility and LevelNameDocumentation
public staticzcx_message (structured type)  

Methods

Visibility and LevelNameDocumentation
public instance
constructor
importingtextidlike IF_T100_MESSAGE=>T100KEY optional
previouslike PREVIOUS optional
CONSTRUCTOR
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcx_rejected_execution.html: -------------------------------------------------------------------------------- 1 | Class ZCX_REJECTED_EXECUTION

Class ZCX_REJECTED_EXECUTION

public inheriting from CX_NO_CHECK final create public

Documentation

Thrown by an executor when a task cannot be accepted.

Interfaces

if_t100_dyn_msg
if_t100_message

Constants

Visibility and LevelNameDocumentation
public staticzcx_message (structured type)  

Methods

Visibility and LevelNameDocumentation
public instance
constructor
importingtextidlike IF_T100_MESSAGE=>T100KEY optional
previouslike PREVIOUS optional
CONSTRUCTOR
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcx_still_running.html: -------------------------------------------------------------------------------- 1 | Class ZCX_STILL_RUNNING

Class ZCX_STILL_RUNNING

public inheriting from CX_NO_CHECK final create public

Documentation

Not a runnable exception

Interfaces

if_t100_dyn_msg
if_t100_message

Constants

Visibility and LevelNameDocumentation
public staticzcx_message (structured type)  

Methods

Visibility and LevelNameDocumentation
public instance
constructor
importingtextidlike IF_T100_MESSAGE=>T100KEY optional
previouslike PREVIOUS optional
CONSTRUCTOR
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcx_thread_execution.html: -------------------------------------------------------------------------------- 1 | Class ZCX_THREAD_EXECUTION

Class ZCX_THREAD_EXECUTION

public inheriting from CX_NO_CHECK final create public

Documentation

Thrown by an executor when a task cannot be accepted.

Interfaces

if_t100_dyn_msg
if_t100_message

Constants

Visibility and LevelNameDocumentation
public staticzcx_message (structured type)  

Methods

Visibility and LevelNameDocumentation
public instance
constructor
importingtextidlike IF_T100_MESSAGE=>T100KEY optional
previouslike PREVIOUS optional
CONSTRUCTOR
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcx_thread_start_fail.html: -------------------------------------------------------------------------------- 1 | Class ZCX_THREAD_START_FAIL

Class ZCX_THREAD_START_FAIL

public inheriting from CX_NO_CHECK final create public

Documentation

Not a runnable exception

Interfaces

if_t100_dyn_msg
if_t100_message

Constants

Visibility and LevelNameDocumentation
public staticzcx_message (structured type)  

Methods

Visibility and LevelNameDocumentation
public instance
constructor
importingtextidlike IF_T100_MESSAGE=>T100KEY optional
previouslike PREVIOUS optional
CONSTRUCTOR
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/classes/zcx_wait_timeout.html: -------------------------------------------------------------------------------- 1 | Class ZCX_WAIT_TIMEOUT

Class ZCX_WAIT_TIMEOUT

public inheriting from CX_NO_CHECK final create public

Documentation

Thrown by an executor when a task cannot be accepted.

Interfaces

if_t100_dyn_msg
if_t100_message

Constants

Visibility and LevelNameDocumentation
public staticzcx_message (structured type)  

Attributes

Visibility and LevelNameDocumentation
public instancewaited type ZETHREAD_WAIT_TIME  

Methods

Visibility and LevelNameDocumentation
public instance
constructor
importingtextidlike IF_T100_MESSAGE=>T100KEY optional
previouslike PREVIOUS optional
waitedtype ZETHREAD_WAIT_TIME optional
CONSTRUCTOR
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/interfaces/zif_executor_service.html: -------------------------------------------------------------------------------- 1 | Interface ZIF_EXECUTOR_SERVICE

Interface ZIF_EXECUTOR_SERVICE

public

Documentation

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 |

Types

Visibility and LevelNameDocumentation
publictty_futures TYPE standard table of ref to zif_future with non-unique default key 
publictty_runnables TYPE standard table of ref to zif_runnable with non-unique default key 
publictty_threads TYPE standard table of ref to zcl_thread with non-unique default key 

Methods

Visibility and LevelNameDocumentation
public instance
await_termination
importingiv_timeouttype ZETHREAD_WAIT_TIME optional

Awaits for all submitted tasks to finish their execution.
5 | Similarly to a thread.join( ), this can be used to wait join back forked worked.
6 |

Parameters
iv_timeout

Timeout for the wait

public instance
invoke_all
importingit_runnablestype TTY_RUNNABLES
returningvalue(rt_futures)type TTY_FUTURES

Submits all runnables for execution according to executor rules and return a list of futures for each started thread.
7 | If a callback is provided, it is called when the runnable finishes its execution
8 | The thread result can be retrieved with the get method of a future. This awaits for the Thread to complete.

9 |
Parameters
it_runnables

Runnables to execute

10 |
rt_futures

Futures

public instanceshutdown

Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.
11 | Does not wait previously submitted tasks to end. Use await_termination for that. 12 |

public instance
shutdown_now
returningvalue(rt_not_ran)type TTY_RUNNABLES

Attempts to stop all actively executing tasks, halts the processing of waiting tasks, 13 | and returns a list of the tasks that were awaiting execution.
14 | Does not wait previously submitted tasks to end. Use await_termination for that. 15 |

Parameters
rt_not_ran

A list of all the runnables that were not executed

public instance
submit
importingio_runnabletype ref to ZIF_RUNNABLE
returningvalue(ro_future)type ref to ZIF_FUTURE

Submits the runnable to run asynchronously in a new thread.
16 | If a callback is provided, it is called when the runnable finishes its execution
17 | The thread result can be retrieved with the get method of a future. This awaits for the Thread to complete.

18 |
Parameters
io_runnable

The runnable to submit

-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/interfaces/zif_future.html: -------------------------------------------------------------------------------- 1 | Interface ZIF_FUTURE

Interface ZIF_FUTURE

public

Documentation

A future representing thre result of asynchronous computation

Methods

Visibility and LevelNameDocumentation
public instance
get
importingiv_timeouttype ZETHREAD_WAIT_TIME optional
returningvalue(ro_result)type ref to ZIF_RUNNABLE_RESULT

Get the future result.
2 | This will make the relevant thread to wait for its completion.
3 | Raises zcx_execution_error in case thread ends in error.

4 | 5 |
Parameters
iv_timeout

Maximum time for wait for resolution.

6 |
ro_result

Thread result

public instance
is_done
returningvalue(rv_result)type ABAP_BOOL

Returns true in case future is resolved.

7 | 8 |
Parameters
rv_result

is done

-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/interfaces/zif_runnable.html: -------------------------------------------------------------------------------- 1 | Interface ZIF_RUNNABLE

Interface ZIF_RUNNABLE

public

Documentation

Runnable interface
2 | A class implementing this interface can be used by a thread to execute the work defined in run. 3 |

Interfaces

if_serializable_object

Methods

Visibility and LevelNameDocumentation
public instance
run
returningvalue(ro_result)type ref to ZIF_RUNNABLE_RESULT

Run
4 | This method is called by a thread asynchronously to execute work.
5 | The returned result must implement the specified interface, this result can be returned by the Thread once its finished.
6 | The thread also passes this result to the callback function, if defined in its creation. 7 |

8 |
Parameters
ro_result

result of runnable

-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/interfaces/zif_runnable_result.html: -------------------------------------------------------------------------------- 1 | Interface ZIF_RUNNABLE_RESULT

Interface ZIF_RUNNABLE_RESULT

public

Documentation

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 |

Interfaces

if_serializable_object
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/interfaces/zif_thread_callback.html: -------------------------------------------------------------------------------- 1 | Interface ZIF_THREAD_CALLBACK

Interface ZIF_THREAD_CALLBACK

public

Documentation

Callback interface


2 | A callback can be provided to threads to handle its results or errors after it finishes 3 | its processing. 4 |

Methods

Visibility and LevelNameDocumentation
public instance
on_error
importingiv_tasknametype CHAR32
io_errortype ref to CX_ROOT

On error method

5 | This method is called in case there was an error in the thread processing. 6 | For it to work properly, only no check exceptions must be raised in the Thread. 7 |
Parameters
iv_taskname

Task name if provided during thread creation or its unique UID

8 |
io_error

Exception raised by the thread execution

public instance
on_result
importingiv_tasknametype CHAR32
io_resulttype ref to ZIF_RUNNABLE_RESULT

Callback method
9 | This method is called in case of a successful execution

10 |
Parameters
iv_taskname

Task name if provided during Thread creation or its unique UID

11 |
io_result

Result of the Thread processing

-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/interfaces/zif_thread_factory.html: -------------------------------------------------------------------------------- 1 | Interface ZIF_THREAD_FACTORY

Interface ZIF_THREAD_FACTORY

public

Documentation

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 |

Methods

Visibility and LevelNameDocumentation
public instance
new_thread
importingio_runnabletype ref to ZIF_RUNNABLE
io_callbacktype ref to ZIF_THREAD_CALLBACK optional
iv_tasknametype CHAR32 optional
returningvalue(ro_result)type ref to ZCL_THREAD

Returns a new thread

5 | 6 |
Parameters
io_runnable

Runnable for the thread

7 |
io_callback

Callback for the thread

8 |
iv_taskname

Task name for the thread

9 |
ro_result

The returned thread.

-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/zthread_example/classes/ztc_thread_example.html: -------------------------------------------------------------------------------- 1 | Class ZTC_THREAD_EXAMPLE

Class ZTC_THREAD_EXAMPLE

public inheriting from ZCL_ASSERT final create public for testing

Documentation

A simple thread example

Interfaces

zif_thread_callback
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/zthread_example/classes/ztc_thread_pool_example.html: -------------------------------------------------------------------------------- 1 | Class ZTC_THREAD_POOL_EXAMPLE

Class ZTC_THREAD_POOL_EXAMPLE

public inheriting from ZCL_ASSERT final create public for testing

Documentation

A simple thread example

Interfaces

zif_thread_callback
-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/zthread_tests/classes/zcl_deactivated_thread.html: -------------------------------------------------------------------------------- 1 | Class ZCL_DEACTIVATED_THREAD

Class ZCL_DEACTIVATED_THREAD

public inheriting from ZCL_THREAD final create public for testing

Documentation

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 |

Methods

Visibility and LevelNameDocumentation
public instance
constructor
importingio_runnabletype ref to ZIF_RUNNABLE
io_callbacktype ref to ZIF_THREAD_CALLBACK optional
iv_tasknametype CHAR32

Deactivated thread constructor.

9 | 10 |
Parameters
io_runnable

Runnable

11 |
io_callback

Callback routine

12 |
iv_taskname

Task name

public instanceget_result redefinition 
public instance
get_runnable
returningvalue(ro_result)type ref to ZIF_RUNNABLE

Returns the runnable for which the Thread was created

13 |
Parameters
ro_result

Thread's runnable

public instanceon_end redefinition 
public instance
set_error
importingio_errortype ref to CX_ROOT

Set the error which will be sent to the callback routine

14 |
Parameters
io_error

the error to be sent back

public instance
set_result
importingio_resulttype ref to ZIF_RUNNABLE_RESULT

Set the result which will be sent to the callback routine

15 |
Parameters
io_result

the result to be sent back

public instancestart redefinition 
public instance
was_started
returningvalue(r_result)type ABAP_BOOL

Allows the verification if the thread was started

16 |
Parameters
r_result

ABAP_TRUE/ABAP_FALSE

-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/zthread_tests/classes/zcl_deactivated_thread_factory.html: -------------------------------------------------------------------------------- 1 | Class ZCL_DEACTIVATED_THREAD_FACTORY

Class ZCL_DEACTIVATED_THREAD_FACTORY

public final create public for testing

Documentation

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 |

. 7 |

Interfaces

zif_thread_factory

Types

Visibility and LevelNameDocumentation
publicsty_thread_result (structured type) 
publictty_threads TYPE standard table of ref to zcl_deactivated_thread with non-unique default key 
publictty_threads_results TYPE standard table of sty_thread_result with non-unique default key 

Methods

Visibility and LevelNameDocumentation
public instance
get_created_threads
returningvalue(rt_result)type TTY_THREADS
 
public instance
set_thread_call_result
importingiv_thread_numtype I
io_resulttype ref to ZIF_RUNNABLE_RESULT

Sets the result to be returned by the iv_thread_num(n) created by the factory.

8 | 9 |
Parameters
iv_thread_num

The thread creation number to have the result

10 |
io_result

The result to be returned by the nth created thread.

-------------------------------------------------------------------------------- /ABAPDoc/abapdoc/zthread/zthread_tests/classes/zcl_runnable_dummy.html: -------------------------------------------------------------------------------- 1 | Class ZCL_RUNNABLE_DUMMY

Class ZCL_RUNNABLE_DUMMY

public final create public for testing

Documentation

A dummy runnable

Interfaces

if_serializable_object
zif_runnable
zif_runnable_result

Methods

Visibility and LevelNameDocumentation
public instance
constructor
importingiv_waittype ZETHREAD_WAIT_TIME optional
 
public instanceraise_on_run  
public instance
was_run_called
returningvalue(r_result)type ABAP_BOOL
 
-------------------------------------------------------------------------------- /ABAPDoc/htmldesign/stylesheet.css: -------------------------------------------------------------------------------- 1 | body { font-family:arial; font-size:13px; background-color: white; margin: 0px; } 2 | table { border: 1px solid black; border-spacing: 0px; border-collapse: collapse; text-align: left; vertical-align: top; width: 100%; } 3 | tr { vertical-align: top; } 4 | th { border: 1px solid black; padding: 4px; background-color: #EBEBEB; text-align: left; } 5 | td { border: 1px solid black; padding: 3px; } 6 | h2 { font-size: 22px; } 7 | h4 { font-size: 18px; } 8 | h5 { font-size: 13px; font-weight: bold; margin-bottom: 5px; } 9 | a { color: black; } 10 | #header { border-bottom: 3px solid grey; background-color: ghostwhite; width: 100%; padding: 4px; padding-left: 8px; } 11 | #headerTitle { padding: 5px; font-size: 22px } 12 | #content { margin: 8px; } 13 | #footer { font-size: 11px; padding-bottom: 8px; padding-left: 8px; } 14 | .identifier { font-weight: bold; text-transform: lowercase; } 15 | .methodSignature { border: 0px; margin: 1px; width: auto; } 16 | .methodSignature tr { text-align: left; vertical-align: top; } 17 | .methodSignature td { border: 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 5px; } 18 | .methodParametersDocumentation { border: 0px; margin-left: 5px; width: auto; } 19 | .methodParametersDocumentation td { border: 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 5px; } 20 | .parametersTable { border: 0px; } 21 | .fieldName {} 22 | .methodName {} 23 | .parameterName {} 24 | .exceptionName {} 25 | .objectName { text-transform: uppercase; } 26 | .interfaceName { font-weight: normal; } 27 | .exceptions { border: 0px; width: auto; } 28 | .exceptions td { border: 0px; padding: 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 5px; } 29 | .abapDocComment p { margin-top: 0px; } 30 | .shorttext { margin-top: 0px; } -------------------------------------------------------------------------------- /ABAPDoc/readme.txt: -------------------------------------------------------------------------------- 1 | Content of this folder: 2 | 3 | ./abapdoc/ 4 | 5 | Contains the exported documentation for the selected classes and interfaces. Each object is stored in its ABAP package e.g. 6 | 7 | +---package 8 | \---subpackage 9 | \---classes 10 | \---cl_abap_class 11 | 12 | 13 | ./htmldesign/ 14 | 15 | Contains the html stylesheet. It can be used to change the design of the generated documentation. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 xinitrc86 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZThread 2 | Simple parallel execution via Threads implemented in ABAP, based on JAVA Thread and its *Runnable* interface plus callback capabilities. 3 | Check the [blog post](https://blogs.sap.com/2021/08/26/async-parallel-abap-in-a-oo-way/) for more details. 4 | Check the ABAPDocs for API information. 5 | ```abap 6 | data(someProcessing) = new zcl_some_processing( some_data ). 7 | data(anotherProcessing) = new zcl_some_other( some_data ). 8 | 9 | new zcl_thread( someProcessing )->start( ). 10 | new zcl_thread( anotherProcessing )->start( ). 11 | 12 | "waits for threads to finish" 13 | zcl_thread=>join_all( ). 14 | 15 | write: 'All done!' 16 | ``` 17 | You can check the ABAP Docs for info and the source for examples. Feel free to provide new examples or colaborate with this. 18 | 19 | ## Usage 20 | Implement the *zif_runnable* interface with the logic you want to *fork* (start in a new thread), give it to a thread and start: 21 | ```abap 22 | class zcl_my_runnable... 23 | interfaces zif_runnable. 24 | 25 | method constructor. 26 | "store data to process 27 | endmethod. 28 | 29 | method zif_runnable~run. 30 | "...process data. 31 | endmethod. 32 | 33 | endclass. 34 | 35 | data(firstSplit) = new zcl_my_runnable( data_to_process_1 ). 36 | data(secondSplit) = new zcl_my_runnable( data_to_process_2 ). 37 | 38 | new zcl_thread( firstSplit )->start( ). 39 | new zcl_thread( secondSplit )->start( ). 40 | ``` 41 | ### Waiting for Threads to end 42 | The static method join_all allows *join* (wait for threads to finish). 43 | ```abap 44 | data(myThread) = new zcl_thread(aRunnable). 45 | new zcl_thread(anotherRunnable)->start( ). 46 | new zcl_thread(yetAnotherRunnable)->start( ). 47 | 48 | myThread->start( ). 49 | myThread->join( ). "waits for this specific thread" 50 | 51 | zcl_thread=>join_all( ). "waits for all threads to finish" 52 | 53 | ``` 54 | ### Retrieving threads results 55 | You can retrieve results back from Threads by implementing the *zif_runnable_result* interface. 56 | ```abap 57 | class zcl_my_runnable definition. 58 | "here runnable is implementing both interfaces" 59 | "but the result can be a separate object too" 60 | interfaces: zif_runnable, zif_runnable_result. 61 | data myNumbers type standard table of p... 62 | data myTotal type p. 63 | 64 | method get_total. 65 | rv_result = myTotal. 66 | endmethod. 67 | 68 | method zif_runnable~run. 69 | "sums numbers into myTotal" 70 | myTotal = reduce #( 71 | init total = 0. 72 | for number in myNumbers 73 | next 74 | total = total + number 75 | ). 76 | "This is possoble because me is implementing 77 | "zif_runnable_result. Could be another object. 78 | ro_result = me. 79 | endmethod. 80 | 81 | endclass. 82 | 83 | data(myRunnable) = new zcl_my_runnable( value #( ( 10 ) ( 10 ) ( 10 ) ) ). 84 | data(myThread) = new zcl_thread( myRunnable ). 85 | 86 | myThread->start( ). 87 | myThread->join( ). 88 | 89 | "retrieves the result, callers must know its type" 90 | data(myResult) = cast zcl_my_runnable( myThread->get_result( ) ). 91 | data(myTotal) = myResult->get_total( ). 92 | 93 | write: myTotal. "30" 94 | 95 | ``` 96 | 97 | ### Defining callbacks 98 | To defined a callback, implement interface *zif_thread_callback|* and passe it along to the thread. For an callback object to be called back, it must still be referenced once the thread is finished. 99 | ```abap 100 | class zcl_callback_example definition. 101 | interfaces zif_thread_callback. 102 | 103 | private section. 104 | data myTotal type p. 105 | 106 | method zif_thread_callback~on_result. 107 | data(result) = cast zcl_my_result( io_result ). 108 | myTotal = myTotal + result->get_total( ). 109 | endmethod. 110 | 111 | method zif_thread_callback~on_error. 112 | raise exception type zcx_myError 113 | exporting previous = io_error. 114 | endmethod. 115 | 116 | method sumAll. 117 | 118 | data(runnable1) = zcl_my_Runnable( value #( ( 10 ) ( 10 ) ( 10 ) ) ). "adds to 30" 119 | data(runnable2) = zcl_my_Runnable( value #( ( 20 ) ( 20 ) ( 20 ) ) ). "adds to 60" 120 | 121 | new zcl_thread( io_runnable = runnable1, io_callback = me )->start( ). 122 | new zcl_thread( io_runnable = runnable2, io_callback = me )->start( ). 123 | 124 | zcl_thread=>join_all( ). 125 | write: myTotal. "sum 30 + 60 = 90 " 126 | 127 | endmethod. 128 | 129 | endclass. 130 | ``` 131 | # Help welcomed 132 | Reviews, examples, downports. Feel free to contribute. 133 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zthread", 3 | "author" : "xinitrc86 ", 4 | "version": "1.0.0", 5 | "description": "Simple Thread implementation for ABAP. Based on JAVA Thread, its Runnable interface with some additional callback capabilities.", 6 | "license" : "MIT", 7 | "repository": { 8 | "type" : "git", 9 | "url" : "https://github.com/xinitrc86/zthread.git" 10 | }, 11 | "scripts": { 12 | "test": "npm run eslint && npm run abaplint", 13 | "merge": "", 14 | "merge.ci": "", 15 | "abaplint": "abaplint", 16 | "eslint": "eslint src" 17 | }, 18 | "devDependencies": { 19 | "@abaplint/cli": "^2.36.6", 20 | "abapmerge": "^0.14.1", 21 | "eslint": "^7.2.0" 22 | } 23 | } -------------------------------------------------------------------------------- /src/example/package.devc.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Simple example with threads 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/example/ztc_thread_example.clas.abap: -------------------------------------------------------------------------------- 1 | class ztc_thread_example definition 2 | public 3 | final 4 | create public 5 | for testing 6 | inheriting from zcl_assert 7 | risk level harmless 8 | duration short. 9 | 10 | public section. 11 | interfaces zif_thread_callback. 12 | protected section. 13 | private section. 14 | data v_final_sum type i. 15 | methods: 16 | it_sums_numbers for testing. 17 | endclass. 18 | 19 | 20 | 21 | class ztc_thread_example implementation. 22 | 23 | method it_sums_numbers. 24 | "creates three runnables, 25 | "have a look on the "local types" for the definition of the runnable and the result 26 | 27 | 28 | 29 | "10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + 90 = 450 30 | 31 | data(lo_runnable1) = new lcl_sum_numbers( value #( 32 | ( 10 ) ( 20 ) ( 30 ) 33 | ) ). 34 | 35 | data(lo_runnable2) = new lcl_sum_numbers( value #( 36 | ( 40 ) ( 50 ) ( 60 ) 37 | ) ). 38 | 39 | data(lo_runnable3) = new lcl_sum_numbers( value #( 40 | ( 70 ) ( 80 ) ( 90 ) 41 | ) ). 42 | 43 | 44 | data lo_thread_factory type ref to zif_thread_factory. 45 | lo_thread_factory = new zcl_default_thread_factory( ). 46 | 47 | "thread (fork) their execution, 48 | "here I'm just passing myself as callback, 49 | "check zif_thread_callback~on_callback below 50 | lo_thread_factory->new_thread( 51 | io_runnable = lo_runnable1 52 | io_callback = me 53 | )->start( ). 54 | 55 | 56 | "other way of creating threasd 57 | new zcl_thread( 58 | io_runnable = lo_runnable2 59 | io_callback = me 60 | )->start( ). 61 | 62 | lo_thread_factory->new_thread( 63 | io_runnable = lo_runnable3 64 | io_callback = me 65 | )->start( ). 66 | 67 | 68 | "wait (join) them and check the sum done by the callback 69 | zcl_thread=>join_all( ). 70 | assert_equals( 71 | exp = 450 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_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_example.clas.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | ZTC_THREAD_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/example/ztc_thread_pool_example.clas.abap: -------------------------------------------------------------------------------- 1 | class ztc_thread_pool_example definition 2 | public 3 | final 4 | create public 5 | for testing 6 | inheriting from zcl_assert 7 | risk level harmless 8 | duration short. 9 | 10 | public section. 11 | interfaces zif_thread_callback. 12 | protected section. 13 | private section. 14 | data v_final_sum type i. 15 | methods: 16 | it_sums_numbers for testing. 17 | endclass. 18 | 19 | 20 | 21 | class ztc_thread_pool_example implementation. 22 | 23 | method it_sums_numbers. 24 | 25 | 26 | "The fixed pool executor will ensure that: no matter how many threads are submitted/invoked 27 | "only 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 | "!

. 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 | "! 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 | --------------------------------------------------------------------------------