├── .abapgit.xml ├── LICENSE ├── README.md └── src ├── package.devc.xml ├── zcl_thread_queue_dispatcher.clas.abap ├── zcl_thread_queue_dispatcher.clas.xml ├── zcl_thread_reducer.clas.abap ├── zcl_thread_reducer.clas.testclasses.abap ├── zcl_thread_reducer.clas.xml ├── zcl_thread_runner_slug.clas.abap ├── zcl_thread_runner_slug.clas.testclasses.abap ├── zcl_thread_runner_slug.clas.xml ├── zthread_runner_test.prog.abap ├── zthread_runner_test.prog.xml ├── zthread_runner_test_simple.prog.abap ├── zthread_runner_test_simple.prog.xml ├── zthreads.fugr.lzthreadstop.abap ├── zthreads.fugr.lzthreadstop.xml ├── zthreads.fugr.saplzthreads.abap ├── zthreads.fugr.saplzthreads.xml ├── zthreads.fugr.xml └── zthreads.fugr.z_thread_runner.abap /.abapgit.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | E 6 | /src/ 7 | PREFIX 8 | 9 | /.gitignore 10 | /LICENSE 11 | /README.md 12 | /package.json 13 | /.travis.yml 14 | /.gitlab-ci.yml 15 | /abaplint.json 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alexander Tsybulsky 4 | Part of the code: Matthew Billingham 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Abap parallel thread utils 2 | 3 | Initially based on this: https://blogs.sap.com/2019/03/19/parallel-processing-made-easy/ 4 | 5 | Started as an attempt to familiarize with abap parallel processing. Ended with a couple of abstract utils worth sharing: a framework to run abap threads with classes, not with function modules. Also proposes an abstraction for reducer queue, allowing to run multiple worker in threads but wait for just one task to end. 6 | 7 | ## Example 8 | 9 | For the full example see [zthread_runner_test.prog.abap](src/zthread_runner_test.prog.abap). It implements various possibilities to use the library: 10 | - single task, started in parallel: directly with FM or with the wrapping class (preferable) 11 | - multiple with queue handling in the main tasks 12 | - multiple with queue handling in a parallel task (reducer) 13 | 14 | Further development might go to the direction of complete **map-reduce** framework (which is already possible in fact, just lacking some `map` abstractions). 15 | 16 | ## Components 17 | 18 | - `zcl_thread_queue_dispatcher` - mostly the code from [here](https://blogs.sap.com/2019/03/19/parallel-processing-made-easy/) with some minor modification. An util to initialize the parallel processing and queueing of workers within a limited number of threads. 19 | - `zcl_thread_runner_slug` - abstraction for a task worker. Works together with FM `ZTHREAD_RUNNER_TEST`. See example in `run_1_parallel` method in [zthread_runner_test.prog.abap](src/zthread_runner_test.prog.abap). A subclass must implement: 20 | - a) `get_state_ref` to return a reference to the worker state. The state is serialized before starting parallel thread, passed serialized inside the thread and **rehydrate a new instance inside the thread**. This is due to the fact that objects cannot travel between threads in abap, just char-like data. So be aware to represent the complete state, that is needed to run the task, in a single structure! In case you want to serialize the state specifically, redefine `serialize/deserialize_state` methods 21 | - b) `run` - code to do the work 22 | - `zcl_thread_reducer` - abstraction for a worker, that starts other workers in other threads. It inherits from `zcl_thread_runner_slug` and supposed to be started in parallel itself. So the idea is to compile some table-like-payload (multiple data portions to work on), put it into the reducer, and send the reducer to a thread itself, then wait for **a one single task** to complete in the main code, which will contain all the results. See example in `run_with_reducer` method in [zthread_runner_test.prog.abap](src/zthread_runner_test.prog.abap). Implement the following abstract methods: 23 | - a) `get_state_ref` - to return a reference to **the payload table** 24 | - b) `create_runner` - code to do create a worker instance - a class inherited from `zcl_thread_runner_slug` 25 | - c) `extract_result` - code to extract result from a worker instance and put into the payload table (which is the final result of the reducer itself) 26 | 27 | **Important**: the sub classes of the `zcl_thread_runner_slug` and `zcl_thread_reducer` must not have constructor with parameters as they are re-instantiated and re-hydrated inside a thread. Use factory pattern or at least `create` class method to create instances. 28 | 29 | ## Technical notes 30 | 31 | The library supposes inheriting and dynamic calls, not reusing of interfaces. This is because of potential usage as fully local classes. 32 | 33 | ## Credits 34 | 35 | - Matthew Billingham, https://blogs.sap.com/2019/03/19/parallel-processing-made-easy/ 36 | -------------------------------------------------------------------------------- /src/package.devc.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Threads utils 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/zcl_thread_queue_dispatcher.clas.abap: -------------------------------------------------------------------------------- 1 | class ZCL_THREAD_QUEUE_DISPATCHER definition 2 | public 3 | final 4 | create public . 5 | 6 | public section. 7 | type-pools abap . 8 | 9 | types: 10 | ty_thread_name_prefix type c length 6 . 11 | types: 12 | ty_thread_name type c length 8 . 13 | 14 | constants c_default_group type rzlli_apcl value 'parallel_generators'. "#EC NOTEXT 15 | constants c_default_task_prefix type ty_thread_name_prefix value 'PARALL'. "#EC NOTEXT 16 | constants c_default_timeout type i value 5. "#EC NOTEXT 17 | constants c_max_threads type i value 20. "#EC NOTEXT 18 | 19 | methods constructor 20 | importing 21 | !i_threads type i 22 | !i_timeout type i default c_default_timeout 23 | !i_task_prefix type ty_thread_name_prefix default c_default_task_prefix 24 | !i_group type rzlli_apcl default c_default_group . 25 | methods is_queue_empty 26 | returning 27 | value(r_empty) type abap_bool . 28 | methods clear_thread 29 | importing 30 | !i_task type ty_thread_name . 31 | methods handle_resource_failure . 32 | methods get_free_thread 33 | returning 34 | value(r_thread) type ty_thread_name . 35 | methods get_server_group 36 | returning 37 | value(r_server_group) type rzlli_apcl . 38 | 39 | protected section. 40 | 41 | private section. 42 | 43 | types: 44 | begin of ty_thread, 45 | thread type ty_thread_name, 46 | used type abap_bool, 47 | end of ty_thread . 48 | 49 | data: 50 | task_prefix type ty_thread_name_prefix, 51 | threads_list type standard table of ty_thread with default key, 52 | threads type i, 53 | used_threads type i, 54 | timeout type i, 55 | group type rzlli_apcl. 56 | 57 | methods get_free_threads 58 | returning 59 | value(r_free_threads) type i . 60 | methods debug 61 | importing 62 | iv_msg type string. 63 | ENDCLASS. 64 | 65 | 66 | 67 | CLASS ZCL_THREAD_QUEUE_DISPATCHER IMPLEMENTATION. 68 | 69 | 70 | method IS_QUEUE_EMPTY. 71 | 72 | debug( |queue->is_queue_empty| ). 73 | r_empty = boolc( used_threads = 0 ). 74 | " potential race condition? add end_of_queue flag ? 75 | 76 | endmethod. 77 | 78 | 79 | method CLEAR_THREAD. 80 | 81 | debug( |queue->clear_thread({ i_task }), { me->used_threads }/{ me->threads }| ). 82 | 83 | field-symbols like line of me->threads_list. 84 | read table me->threads_list assigning 85 | with key 86 | thread = i_task 87 | used = abap_true. 88 | -used = abap_false. 89 | used_threads = used_threads - 1. 90 | endmethod. 91 | 92 | 93 | method CONSTRUCTOR. 94 | me->group = i_group. 95 | me->task_prefix = i_task_prefix. 96 | me->timeout = i_timeout. 97 | 98 | if i_threads > c_max_threads. 99 | me->threads = 20. 100 | elseif i_threads < 1. 101 | me->threads = 1. 102 | else. 103 | me->threads = i_threads. 104 | endif. 105 | 106 | data free_threads type i. 107 | free_threads = me->get_free_threads( ). 108 | 109 | " Ensure that no more than half of the free threads are used 110 | me->threads = nmin( val1 = me->threads val2 = free_threads div 2 + 1 ). 111 | 112 | debug( |queue->constructor, [want { i_threads }, free { free_threads }] ~> { me->threads }| ). 113 | 114 | " Initialise threads 115 | data thread_no type n length 2 value '00'. 116 | field-symbols like line of me->threads_list. 117 | do me->threads times. 118 | append initial line to me->threads_list assigning . 119 | -thread = me->task_prefix && thread_no. 120 | -used = abap_false. 121 | add 1 to thread_no. 122 | enddo. 123 | endmethod. 124 | 125 | 126 | method debug. 127 | * field-symbols type string_table. 128 | * assign ('(ZTHREAD_RUNNER_TEST_MULTI_ONLY)gt_log') to . 129 | * if sy-subrc is initial. 130 | * append iv_msg to . 131 | * endif. 132 | endmethod. 133 | 134 | 135 | method GET_FREE_THREAD. 136 | " Wait for a free thread 137 | 138 | debug( |queue->get_free_thread, { me->used_threads }/{ me->threads }| ). 139 | 140 | if me->used_threads = me->threads. 141 | wait until me->used_threads < me->threads up to me->timeout seconds. 142 | data(err) = sy-subrc. 143 | debug( |queue->get_free_thread, after wait, rc={ err }| ). 144 | if err = 8. "Timeout 145 | assert 1 = 0. 146 | "TODO ??? 147 | endif. 148 | endif. 149 | 150 | " Get number of first free thread 151 | field-symbols like line of me->threads_list. 152 | read table me->threads_list with key used = abap_false assigning . 153 | 154 | -used = abap_true. 155 | r_thread = -thread. 156 | add 1 to used_threads. 157 | endmethod. 158 | 159 | 160 | method GET_FREE_THREADS. 161 | " Get number of free threads 162 | call function 'SPBT_INITIALIZE' 163 | exporting 164 | group_name = me->group 165 | importing 166 | free_pbt_wps = r_free_threads 167 | exceptions 168 | invalid_group_name = 1 169 | internal_error = 2 170 | pbt_env_already_initialized = 3 171 | currently_no_resources_avail = 4 172 | no_pbt_resources_found = 5 173 | cant_init_different_pbt_groups = 6 174 | others = 7. 175 | 176 | " TODO graceful handling of error 177 | case sy-subrc. 178 | when 0. " Do nothing 179 | 180 | when 3. 181 | " Already initialised - get current number of free threads 182 | call function 'SPBT_GET_CURR_RESOURCE_INFO' 183 | importing 184 | free_pbt_wps = r_free_threads 185 | exceptions 186 | internal_error = 1 187 | pbt_env_not_initialized_yet = 2 188 | others = 3. 189 | 190 | if sy-subrc is not initial. 191 | " Something has gone seriously wrong, so end it here. 192 | message id sy-msgid type 'X' number sy-msgno with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. 193 | endif. 194 | 195 | when others. 196 | " Something has gone seriously wrong, so end it here. 197 | message id sy-msgid type 'X' number sy-msgno with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. 198 | 199 | endcase. 200 | endmethod. 201 | 202 | 203 | method GET_SERVER_GROUP. 204 | r_server_group = me->group. 205 | endmethod. 206 | 207 | 208 | method HANDLE_RESOURCE_FAILURE. 209 | data free_threads type i. 210 | free_threads = me->get_free_threads( ). 211 | if free_threads <= 1 and me->threads > 1. 212 | me->threads = me->threads - 1. 213 | endif. 214 | 215 | wait up to 1 seconds. " Long enough for the system to update 216 | wait until me->used_threads lt me->threads. " Now there's an available thread 217 | endmethod. 218 | ENDCLASS. 219 | -------------------------------------------------------------------------------- /src/zcl_thread_queue_dispatcher.clas.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | ZCL_THREAD_QUEUE_DISPATCHER 7 | E 8 | Therad handler 9 | 1 10 | X 11 | X 12 | X 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/zcl_thread_reducer.clas.abap: -------------------------------------------------------------------------------- 1 | class ZCL_THREAD_REDUCER definition 2 | public 3 | inheriting from ZCL_THREAD_RUNNER_SLUG 4 | abstract 5 | create public . 6 | 7 | public section. 8 | 9 | types: 10 | begin of ty_state, 11 | threads type i, 12 | task_timeout type i, 13 | reduce_timeout type i, 14 | task_prefix type zcl_thread_queue_dispatcher=>ty_thread_name_prefix, 15 | server_group type rzlli_apcl, 16 | end of ty_state . 17 | types: 18 | begin of ty_state_with_payload, 19 | state type ty_state, 20 | payload_buffer type xstring, 21 | end of ty_state_with_payload . 22 | types: 23 | begin of ty_queue, 24 | runner type ref to zcl_thread_runner_slug, 25 | end of ty_queue . 26 | 27 | constants c_default_reduce_timeout type i value 300. "#EC NOTEXT 28 | 29 | methods set_run_params " constructor must be without params 30 | importing 31 | !iv_threads type i 32 | !iv_task_timeout type i default zcl_thread_queue_dispatcher=>c_default_timeout 33 | !iv_reduce_timeout type i default c_default_reduce_timeout 34 | !iv_task_prefix type ty_state-task_prefix default zcl_thread_queue_dispatcher=>c_default_task_prefix 35 | !iv_server_group type ty_state-server_group default zcl_thread_queue_dispatcher=>c_default_group . 36 | methods create_runner 37 | abstract 38 | importing 39 | !io_queue_dispatcher type ref to zcl_thread_queue_dispatcher 40 | !iv_index type i 41 | !iv_payload type data 42 | returning 43 | value(ro_instance) type ref to zcl_thread_runner_slug . 44 | methods extract_result 45 | abstract 46 | importing 47 | !io_runner type ref to zcl_thread_runner_slug 48 | !iv_index type i 49 | changing 50 | !cv_payload type data . 51 | 52 | methods deserialize_state 53 | redefinition . 54 | methods run 55 | redefinition . 56 | methods serialize_state 57 | redefinition . 58 | protected section. 59 | 60 | data ms_state type ty_state. " TODO reconsider visibility ???? 61 | 62 | private section. 63 | ENDCLASS. 64 | 65 | 66 | 67 | CLASS ZCL_THREAD_REDUCER IMPLEMENTATION. 68 | 69 | 70 | method deserialize_state. 71 | data ls_state_w_payload type ty_state_with_payload. 72 | data lv_ref type ref to data. 73 | field-symbols type data. 74 | 75 | assign ls_state_w_payload to . 76 | import data = from data buffer iv_xstr. 77 | assert sy-subrc = 0. 78 | 79 | lv_ref = get_state_ref( ). 80 | assign lv_ref->* to . 81 | import data = from data buffer ls_state_w_payload-payload_buffer. 82 | assert sy-subrc = 0. 83 | ms_state = ls_state_w_payload-state. 84 | endmethod. 85 | 86 | 87 | method run. 88 | 89 | data lv_ref type ref to data. 90 | field-symbols type any table. 91 | field-symbols type data. 92 | 93 | data lo_dispatcher type ref to zcl_thread_queue_dispatcher. 94 | create object lo_dispatcher 95 | exporting 96 | i_threads = ms_state-threads 97 | i_task_prefix = ms_state-task_prefix 98 | i_group = ms_state-server_group 99 | i_timeout = ms_state-task_timeout. 100 | 101 | data lt_queue type standard table of ty_queue. 102 | field-symbols like line of lt_queue. 103 | 104 | lv_ref = get_state_ref( ). 105 | assign lv_ref->* to . 106 | 107 | loop at assigning . 108 | append initial line to lt_queue assigning . 109 | -runner = create_runner( 110 | io_queue_dispatcher = lo_dispatcher 111 | iv_index = sy-tabix 112 | iv_payload = ). 113 | -runner->run_parallel( ). 114 | endloop. 115 | wait until lo_dispatcher->is_queue_empty( ) = abap_true up to 20 seconds. 116 | 117 | loop at assigning . 118 | read table lt_queue assigning index sy-tabix. 119 | assert sy-subrc = 0. 120 | extract_result( 121 | exporting 122 | io_runner = -runner 123 | iv_index = sy-tabix 124 | changing 125 | cv_payload = ). 126 | endloop. 127 | 128 | endmethod. 129 | 130 | 131 | method serialize_state. 132 | data ls_state_w_payload type ty_state_with_payload. 133 | data lv_ref type ref to data. 134 | field-symbols type data. 135 | 136 | ls_state_w_payload-state = ms_state. 137 | lv_ref = get_state_ref( ). 138 | assign lv_ref->* to . 139 | export data = to data buffer ls_state_w_payload-payload_buffer. 140 | assert sy-subrc = 0. 141 | 142 | assign ls_state_w_payload to . 143 | export data = to data buffer rv_xstr. 144 | assert sy-subrc = 0. 145 | endmethod. 146 | 147 | 148 | method set_run_params. 149 | ms_state-threads = iv_threads. 150 | ms_state-task_timeout = iv_task_timeout. 151 | ms_state-reduce_timeout = iv_reduce_timeout. 152 | ms_state-task_prefix = iv_task_prefix. 153 | ms_state-server_group = iv_server_group. 154 | endmethod. 155 | ENDCLASS. 156 | -------------------------------------------------------------------------------- /src/zcl_thread_reducer.clas.testclasses.abap: -------------------------------------------------------------------------------- 1 | class ltct_test definition deferred. 2 | 3 | class ltct_test_model definition 4 | inheriting from zcl_thread_reducer 5 | for testing 6 | duration short 7 | risk level harmless 8 | friends ltct_test. 9 | 10 | public section. 11 | 12 | types: 13 | begin of ty_payload, 14 | index type i, 15 | line type string, 16 | end of ty_payload. 17 | 18 | data mt_payload type standard table of ty_payload. 19 | 20 | methods get_state_ref redefinition. 21 | methods create_runner redefinition. 22 | methods extract_result redefinition. 23 | 24 | endclass. 25 | 26 | class ltct_test_model implementation. 27 | 28 | method create_runner. 29 | endmethod. 30 | method extract_result. 31 | endmethod. 32 | 33 | method get_state_ref. 34 | get reference of mt_payload into rv_ref. 35 | endmethod. 36 | 37 | endclass. 38 | 39 | class ltct_test definition 40 | for testing 41 | duration short 42 | risk level harmless. 43 | private section. 44 | methods serialization for testing. 45 | endclass. 46 | 47 | class ltct_test implementation. 48 | 49 | method serialization. 50 | 51 | data lo type ref to ltct_test_model. 52 | data lv_xstr type xstring. 53 | data ls_state like lo->ms_state. 54 | data lt_payload like lo->mt_payload. 55 | field-symbols

like line of lo->mt_payload. 56 | 57 | create object lo. 58 | lo->set_run_params( iv_threads = 5 ). 59 | append initial line to lt_payload assigning

. 60 |

-index = 1. 61 |

-line = 'Hello'. 62 | append initial line to lt_payload assigning

. 63 |

-index = 2. 64 |

-line = 'World'. 65 | lo->mt_payload = lt_payload. 66 | 67 | cl_abap_unit_assert=>assert_equals( 68 | act = lo->ms_state-threads 69 | exp = 5 ). 70 | 71 | ls_state = lo->ms_state. 72 | 73 | lv_xstr = lo->serialize_state( ). 74 | clear lo->ms_state. 75 | clear lo->mt_payload. 76 | lo->deserialize_state( lv_xstr ). 77 | 78 | cl_abap_unit_assert=>assert_equals( 79 | act = lo->ms_state 80 | exp = ls_state ). 81 | 82 | cl_abap_unit_assert=>assert_equals( 83 | act = lo->mt_payload 84 | exp = lt_payload ). 85 | 86 | endmethod. 87 | 88 | endclass. 89 | -------------------------------------------------------------------------------- /src/zcl_thread_reducer.clas.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | ZCL_THREAD_REDUCER 7 | E 8 | Thread reduce dispatcher 9 | 1 10 | X 11 | X 12 | X 13 | X 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/zcl_thread_runner_slug.clas.abap: -------------------------------------------------------------------------------- 1 | class ZCL_THREAD_RUNNER_SLUG definition 2 | public 3 | abstract 4 | create public . 5 | 6 | public section. 7 | 8 | constants c_runner_fm_name type string value 'Z_THREAD_RUNNER'. "#EC NOTEXT 9 | 10 | methods run 11 | abstract . 12 | methods get_state_ref 13 | abstract 14 | returning 15 | value(rv_ref) type ref to data . 16 | methods serialize_state 17 | returning 18 | value(rv_xstr) type xstring . 19 | methods deserialize_state 20 | importing 21 | !iv_xstr type xstring . 22 | methods run_parallel 23 | importing 24 | !iv_thread_name type clike optional . 25 | methods on_end_of_task 26 | importing 27 | !p_task type clike . 28 | methods is_ready 29 | returning 30 | value(rv_yes) type abap_bool . 31 | methods has_error 32 | returning 33 | value(rv_yes) type abap_bool . 34 | methods error 35 | returning 36 | value(rv_error) type string . 37 | methods set_dispatcher 38 | importing 39 | !io_dispatcher type ref to zcl_thread_queue_dispatcher . 40 | protected section. 41 | 42 | data mv_error type string. 43 | data mv_ready type abap_bool. 44 | data mo_queue_dispatcher type ref to zcl_thread_queue_dispatcher. 45 | 46 | methods debug 47 | importing 48 | iv_msg type string. 49 | 50 | private section. 51 | ENDCLASS. 52 | 53 | 54 | 55 | CLASS ZCL_THREAD_RUNNER_SLUG IMPLEMENTATION. 56 | 57 | 58 | method debug. 59 | * field-symbols type string_table. 60 | * assign ('(ZTHREAD_RUNNER_TEST_MULTI_ONLY)gt_log') to . 61 | * if sy-subrc is initial. 62 | * append iv_msg to . 63 | * endif. 64 | endmethod. 65 | 66 | 67 | method deserialize_state. 68 | data lv_ref type ref to data. 69 | field-symbols type any. 70 | 71 | lv_ref = get_state_ref( ). 72 | assign lv_ref->* to . 73 | import data = from data buffer iv_xstr. 74 | assert sy-subrc = 0. 75 | endmethod. 76 | 77 | 78 | method error. 79 | rv_error = mv_error. 80 | endmethod. 81 | 82 | 83 | method has_error. 84 | rv_yes = boolc( mv_error is not initial ). 85 | endmethod. 86 | 87 | 88 | method is_ready. 89 | rv_yes = mv_ready. 90 | endmethod. 91 | 92 | 93 | method on_end_of_task. 94 | 95 | data lv_xstr type xstring. 96 | 97 | receive results from function c_runner_fm_name 98 | importing 99 | ev_raw_result = lv_xstr 100 | exceptions 101 | others = 4. 102 | 103 | if sy-subrc <> 0. 104 | mv_error = |{ sy-msgv1 }{ sy-msgv2 }{ sy-msgv3 }{ sy-msgv3 }|. 105 | else. 106 | deserialize_state( lv_xstr ). 107 | endif. 108 | 109 | mv_ready = abap_true. 110 | if mo_queue_dispatcher is bound. " Queued thread 111 | mo_queue_dispatcher->clear_thread( |{ p_task }| ). 112 | endif. 113 | 114 | endmethod. 115 | 116 | 117 | method run_parallel. 118 | 119 | data lv_class_name type string. 120 | data lv_xstr type xstring. 121 | data lv_thread_name type string. 122 | data lv_server_group type rzlli_apcl. 123 | 124 | lv_server_group = zcl_thread_queue_dispatcher=>c_default_group. 125 | lv_thread_name = iv_thread_name. 126 | if mo_queue_dispatcher is bound. " Queued thread 127 | lv_server_group = mo_queue_dispatcher->get_server_group( ). 128 | lv_thread_name = mo_queue_dispatcher->get_free_thread( ). 129 | endif. 130 | assert lv_thread_name is not initial. 131 | 132 | lv_class_name = cl_abap_typedescr=>describe_by_object_ref( me )->absolute_name. 133 | lv_xstr = serialize_state( ). 134 | 135 | debug( |runner->run_parallel({ lv_thread_name }), pre start| ). 136 | 137 | call function c_runner_fm_name 138 | starting new task lv_thread_name 139 | destination in group lv_server_group 140 | calling on_end_of_task on end of task 141 | exporting 142 | iv_runner_class_name = lv_class_name 143 | iv_raw_params = lv_xstr 144 | exceptions 145 | communication_failure = 1 146 | system_failure = 2 147 | resource_failure = 3 148 | others = 4. 149 | 150 | data(rc) = sy-subrc. 151 | 152 | if rc = 3. 153 | data r_free_threads type i. 154 | CALL FUNCTION 'SPBT_GET_CURR_RESOURCE_INFO' 155 | IMPORTING 156 | free_pbt_wps = r_free_threads 157 | EXCEPTIONS 158 | internal_error = 1 159 | pbt_env_not_initialized_yet = 2 160 | OTHERS = 3. 161 | debug( |runner->run_parallel({ lv_thread_name }), resource_failure, free_threads = { r_free_threads }, rc={ sy-subrc }| ). 162 | * if r_free_threads = 0 or sy-subrc <> 0. 163 | * debug( ' ^^^^^^^^^^^^^^^^^^' ). 164 | * endif. 165 | endif. 166 | 167 | if rc <> 0. 168 | mv_error = |starting new task failed with rc={ rc }: { sy-msgv1 }{ sy-msgv2 }{ sy-msgv3 }{ sy-msgv3 }|. 169 | mv_ready = abap_true. 170 | debug( |runner->run_parallel({ lv_thread_name }), failed: { mv_error }| ). 171 | if mo_queue_dispatcher is bound. " Queued thread 172 | mo_queue_dispatcher->clear_thread( |{ lv_thread_name }| ). 173 | endif. 174 | else. 175 | debug( |runner->run_parallel({ lv_thread_name }), started ok| ). 176 | endif. 177 | 178 | endmethod. 179 | 180 | 181 | method serialize_state. 182 | data lv_ref type ref to data. 183 | field-symbols type any. 184 | 185 | lv_ref = get_state_ref( ). 186 | assign lv_ref->* to . 187 | export data = to data buffer rv_xstr. 188 | assert sy-subrc = 0. 189 | endmethod. 190 | 191 | 192 | method set_dispatcher. 193 | mo_queue_dispatcher = io_dispatcher. 194 | endmethod. 195 | ENDCLASS. 196 | -------------------------------------------------------------------------------- /src/zcl_thread_runner_slug.clas.testclasses.abap: -------------------------------------------------------------------------------- 1 | class ltct_test_model definition 2 | inheriting from zcl_thread_runner_slug 3 | for testing 4 | duration short 5 | risk level harmless. 6 | 7 | public section. 8 | 9 | types: 10 | begin of ty_state, 11 | hello type string, 12 | world type string, 13 | end of ty_state. 14 | 15 | data ms_state type ty_state. 16 | 17 | methods get_state_ref redefinition. 18 | methods run redefinition. 19 | 20 | endclass. 21 | 22 | class ltct_test_model implementation. 23 | 24 | method run. 25 | endmethod. 26 | 27 | method get_state_ref. 28 | get reference of ms_state into rv_ref. 29 | endmethod. 30 | 31 | endclass. 32 | 33 | class ltct_test definition 34 | for testing 35 | duration short 36 | risk level harmless. 37 | private section. 38 | methods serialization for testing. 39 | endclass. 40 | 41 | class ltct_test implementation. 42 | 43 | method serialization. 44 | 45 | data lo type ref to ltct_test_model. 46 | data ls_temp like lo->ms_state. 47 | data lv_xstr type xstring. 48 | 49 | create object lo. 50 | lo->ms_state-hello = 'Hello'. 51 | lo->ms_state-world = 'World'. 52 | 53 | lv_xstr = lo->serialize_state( ). 54 | import data = ls_temp from data buffer lv_xstr. 55 | cl_abap_unit_assert=>assert_equals( 56 | act = ls_temp 57 | exp = lo->ms_state ). 58 | 59 | clear lo->ms_state. 60 | lo->deserialize_state( lv_xstr ). 61 | cl_abap_unit_assert=>assert_equals( 62 | act = lo->ms_state 63 | exp = ls_temp ). 64 | 65 | endmethod. 66 | 67 | endclass. 68 | -------------------------------------------------------------------------------- /src/zcl_thread_runner_slug.clas.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | ZCL_THREAD_RUNNER_SLUG 7 | E 8 | thread runner abstarct base 9 | 1 10 | X 11 | X 12 | X 13 | X 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/zthread_runner_test.prog.abap: -------------------------------------------------------------------------------- 1 | program zthread_runner_test. 2 | 3 | class lcx_error definition inheriting from cx_no_check. 4 | endclass. 5 | 6 | class lcl_task definition final inheriting from zcl_thread_runner_slug. 7 | public section. 8 | 9 | class-methods create " Constructor MUST be without params 10 | importing 11 | id type i 12 | name type string 13 | trigger_err type abap_bool default abap_false 14 | returning 15 | value(ro_instance) type ref to lcl_task. 16 | 17 | methods result 18 | returning 19 | value(rv_result) type string. 20 | 21 | methods run redefinition. 22 | methods get_state_ref redefinition. 23 | 24 | types: 25 | begin of ty_state, 26 | id type i, 27 | name type string, 28 | trigger_err type abap_bool, 29 | result type string, 30 | end of ty_state. 31 | 32 | private section. 33 | data ms_state type ty_state. 34 | 35 | endclass. 36 | 37 | class lcl_task implementation. 38 | 39 | method create. 40 | create object ro_instance. 41 | ro_instance->ms_state-id = id. 42 | ro_instance->ms_state-name = name. 43 | ro_instance->ms_state-trigger_err = trigger_err. 44 | endmethod. 45 | 46 | method result. 47 | rv_result = ms_state-result. 48 | endmethod. 49 | 50 | method get_state_ref. 51 | get reference of ms_state into rv_ref. 52 | endmethod. 53 | 54 | method run. 55 | if ms_state-trigger_err = abap_true. 56 | raise exception type lcx_error. 57 | else. 58 | ms_state-result = |{ ms_state-id }: name = { ms_state-name }, time = { sy-uzeit }|. 59 | endif. 60 | endmethod. 61 | 62 | endclass. 63 | 64 | ********************************************************************** 65 | 66 | class lcl_reducer definition final inheriting from zcl_thread_reducer. 67 | public section. 68 | 69 | types: 70 | begin of ty_subject, 71 | payload type string, 72 | task type string, 73 | error type string, 74 | result type string, 75 | end of ty_subject, 76 | tt_subject type standard table of ty_subject with key task. 77 | 78 | class-methods create " Constructor MUST be without params 79 | importing 80 | iv_threads type i 81 | it_subjects type tt_subject 82 | returning 83 | value(ro_instance) type ref to lcl_reducer. 84 | 85 | methods result 86 | returning 87 | value(rt_result) type tt_subject. 88 | 89 | methods get_state_ref redefinition. 90 | methods create_runner redefinition. 91 | methods extract_result redefinition. 92 | 93 | private section. 94 | data mt_subjects type tt_subject. 95 | 96 | endclass. 97 | 98 | class lcl_reducer implementation. 99 | 100 | method create. 101 | create object ro_instance. 102 | ro_instance->set_run_params( iv_threads = iv_threads ). 103 | ro_instance->mt_subjects = it_subjects. 104 | endmethod. 105 | 106 | method get_state_ref. 107 | get reference of mt_subjects into rv_ref. 108 | endmethod. 109 | 110 | method result. 111 | rt_result = mt_subjects. 112 | endmethod. 113 | 114 | method create_runner. 115 | 116 | ro_instance = lcl_task=>create( 117 | id = iv_index 118 | trigger_err = boolc( iv_index = 4 ) "one random task failed 119 | name = |Task { iv_index }| ). 120 | ro_instance->set_dispatcher( io_queue_dispatcher ). 121 | 122 | endmethod. 123 | 124 | method extract_result. 125 | 126 | data lo_runner type ref to lcl_task. 127 | field-symbols like line of mt_subjects. 128 | 129 | assign cv_payload to . 130 | lo_runner ?= io_runner. 131 | -task = iv_index. 132 | -result = lo_runner->result( ). 133 | -error = lo_runner->error( ). 134 | 135 | endmethod. 136 | 137 | endclass. 138 | 139 | ********************************************************************** 140 | * controller 141 | ********************************************************************** 142 | 143 | class lcl_main definition final. 144 | public section. 145 | 146 | types: 147 | begin of ty_result, 148 | task type string, 149 | runner type ref to lcl_task, 150 | end of ty_result. 151 | 152 | methods run_single. 153 | methods run_1_parallel. 154 | methods run_multiple. 155 | methods run_with_reducer. 156 | 157 | endclass. 158 | 159 | class lcl_main implementation. 160 | 161 | method run_single. 162 | 163 | write: / 'Running same thread'. 164 | 165 | data lo_task type ref to lcl_task. 166 | lo_task = lcl_task=>create( 167 | id = 1 168 | name = 'Vasya' ). 169 | 170 | data lv_xstr type xstring. 171 | lv_xstr = lo_task->serialize_state( ). 172 | 173 | data lv_class_name type string. 174 | lv_class_name = cl_abap_typedescr=>describe_by_object_ref( lo_task )->absolute_name. 175 | 176 | call function zcl_thread_runner_slug=>c_runner_fm_name 177 | exporting 178 | iv_runner_class_name = lv_class_name 179 | iv_raw_params = lv_xstr 180 | importing 181 | ev_raw_result = lv_xstr 182 | exceptions 183 | other = 4. 184 | 185 | if sy-subrc is not initial. 186 | data errmsg type c length 255. 187 | concatenate sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 into errmsg. 188 | write: / 'Error:', errmsg. 189 | else. 190 | lo_task->deserialize_state( lv_xstr ). 191 | data lv_msg type string. 192 | lv_msg = lo_task->result( ). 193 | write: / lv_msg. 194 | endif. 195 | 196 | endmethod. 197 | 198 | method run_1_parallel. 199 | 200 | data lv_msg type string. 201 | data lo_task type ref to lcl_task. 202 | write: / 'Running parallel'. 203 | 204 | lo_task = lcl_task=>create( 205 | id = 1 206 | * trigger_err = abap_true 207 | name = 'Vasya' ). 208 | 209 | lo_task->run_parallel( 'thread1' ). 210 | wait until lo_task->is_ready( ) = abap_true up to 10 seconds. 211 | 212 | if lo_task->error( ) is not initial. 213 | lv_msg = lo_task->error( ). 214 | write: / 'Error:', lv_msg. 215 | else. 216 | lv_msg = lo_task->result( ). 217 | write: / 'Result:', lv_msg. 218 | endif. 219 | 220 | endmethod. 221 | 222 | method run_multiple. 223 | 224 | write: / 'Running multiple'. 225 | 226 | data lt_results type standard table of ty_result. 227 | data lv_msg type string. 228 | data lo_dispatcher type ref to zcl_thread_queue_dispatcher. 229 | field-symbols like line of lt_results. 230 | 231 | create object lo_dispatcher 232 | exporting 233 | i_threads = 2. 234 | 235 | do 10 times. 236 | append initial line to lt_results assigning . 237 | -task = sy-tabix. 238 | -runner = lcl_task=>create( 239 | id = sy-tabix 240 | trigger_err = boolc( sy-tabix = 3 ) "one random task failed 241 | name = |Task { sy-tabix }| ). 242 | -runner->set_dispatcher( lo_dispatcher ). 243 | -runner->run_parallel( ). 244 | 245 | enddo. 246 | wait until lo_dispatcher->is_queue_empty( ) = abap_true up to 20 seconds. 247 | 248 | loop at lt_results assigning . 249 | if -runner->has_error( ) = abap_false. 250 | lv_msg = -runner->result( ). 251 | write: / 'OK:', -task, lv_msg. 252 | else. 253 | lv_msg = -runner->error( ). 254 | write: / 'ER:', -task, lv_msg. 255 | endif. 256 | endloop. 257 | 258 | endmethod. 259 | 260 | method run_with_reducer. 261 | 262 | write: / 'Running parallel with reducer'. 263 | 264 | " Payload 265 | data lt_subjects type lcl_reducer=>tt_subject. 266 | field-symbols like line of lt_subjects. 267 | do 8 times. 268 | append initial line to lt_subjects assigning . 269 | -payload = sy-tabix. 270 | enddo. 271 | 272 | data lo_reducer type ref to lcl_reducer. 273 | lo_reducer = lcl_reducer=>create( 274 | iv_threads = 2 275 | it_subjects = lt_subjects ). 276 | 277 | lo_reducer->run_parallel( 'reducer' ). 278 | wait until lo_reducer->is_ready( ) = abap_true up to 10 seconds. 279 | 280 | data lt_results type lcl_reducer=>tt_subject. 281 | field-symbols like line of lt_results. 282 | 283 | lt_results = lo_reducer->result( ). 284 | 285 | loop at lt_results assigning . 286 | if -error is not initial. 287 | write: / 'Error :', -task, -error. 288 | else. 289 | write: / 'Result:', -task, -result. 290 | endif. 291 | endloop. 292 | 293 | endmethod. 294 | 295 | endclass. 296 | 297 | form main. 298 | data lo_app type ref to lcl_main. 299 | create object lo_app. 300 | 301 | write: / 'Started'. 302 | uline. 303 | lo_app->run_single( ). 304 | uline. 305 | lo_app->run_1_parallel( ). 306 | uline. 307 | lo_app->run_multiple( ). 308 | uline. 309 | lo_app->run_with_reducer( ). 310 | uline. 311 | write: / 'Finished'. 312 | endform. 313 | 314 | start-of-selection. 315 | perform main. 316 | -------------------------------------------------------------------------------- /src/zthread_runner_test.prog.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | ZTHREAD_RUNNER_TEST 7 | 1 8 | E 9 | X 10 | X 11 | 12 | 13 | 14 | R 15 | Program ZTHREADS_TEST 16 | 21 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/zthread_runner_test_simple.prog.abap: -------------------------------------------------------------------------------- 1 | program zthread_runner_test_simple. 2 | 3 | * This programs uses 7.4 abap syntax ! It is OK to leave it inactive, it is just an example 4 | 5 | class lcl_task definition final inheriting from zcl_thread_runner_slug. 6 | public section. 7 | 8 | class-methods create " Constructor MUST be without params 9 | importing 10 | number_to_process type i " Parameter to process in thread 11 | returning 12 | value(ro_instance) type ref to lcl_task. 13 | 14 | methods result 15 | returning 16 | value(rv_result) type string. 17 | 18 | methods run redefinition. 19 | methods get_state_ref redefinition. 20 | 21 | types: 22 | begin of ty_state, 23 | number_to_process type i, " Parameter to work on 24 | result type string, " Result to return, both should be in one structure 25 | end of ty_state. 26 | 27 | private section. 28 | data ms_state type ty_state. " Worker state, NECESSARY AND SUFFICIENT to run thread worker 29 | " A worker class MAY have other member but they will stay 30 | " just in the main thread so must be related to 31 | " pre/post processing not to the parallel job 32 | endclass. 33 | 34 | class lcl_task implementation. 35 | 36 | method create. 37 | create object ro_instance. 38 | ro_instance->ms_state-number_to_process = number_to_process. 39 | endmethod. 40 | 41 | method result. 42 | rv_result = ms_state-result. 43 | endmethod. 44 | 45 | method get_state_ref. 46 | rv_ref = ref #( ms_state ). 47 | endmethod. 48 | 49 | method run. 50 | ms_state-result = |Hello from worker { ms_state-number_to_process }, time = { sy-uzeit }|. 51 | endmethod. 52 | 53 | endclass. 54 | 55 | ********************************************************************** 56 | 57 | class lcl_reducer definition final inheriting from zcl_thread_reducer. 58 | public section. 59 | 60 | types: 61 | begin of ty_subject, 62 | number_to_process type i, 63 | task type string, " Just for log 64 | error type string, 65 | result type string, 66 | end of ty_subject, 67 | tt_subject type standard table of ty_subject with key task. 68 | 69 | class-methods create " Constructor MUST be without params 70 | importing 71 | iv_threads type i 72 | it_subjects type tt_subject 73 | returning 74 | value(ro_instance) type ref to lcl_reducer. 75 | 76 | methods result 77 | returning 78 | value(rt_result) type tt_subject. 79 | 80 | methods get_state_ref redefinition. 81 | methods create_runner redefinition. 82 | methods extract_result redefinition. 83 | 84 | private section. 85 | data mt_subjects type tt_subject. 86 | 87 | endclass. 88 | 89 | class lcl_reducer implementation. 90 | 91 | method create. 92 | create object ro_instance. 93 | ro_instance->set_run_params( iv_threads = iv_threads ). 94 | ro_instance->mt_subjects = it_subjects. 95 | endmethod. 96 | 97 | method get_state_ref. 98 | get reference of mt_subjects into rv_ref. 99 | endmethod. 100 | 101 | method result. 102 | rt_result = mt_subjects. 103 | endmethod. 104 | 105 | method create_runner. 106 | 107 | field-symbols like line of mt_subjects. 108 | assign iv_payload to . 109 | ro_instance = lcl_task=>create( number_to_process = -number_to_process ). 110 | ro_instance->set_dispatcher( io_queue_dispatcher ). 111 | 112 | endmethod. 113 | 114 | method extract_result. 115 | 116 | data(lo_runner) = cast lcl_task( io_runner ). 117 | 118 | field-symbols like line of mt_subjects. 119 | assign cv_payload to . 120 | -task = iv_index. 121 | -result = lo_runner->result( ). 122 | -error = lo_runner->error( ). 123 | 124 | endmethod. 125 | 126 | endclass. 127 | 128 | ********************************************************************** 129 | * controller 130 | ********************************************************************** 131 | 132 | class lcl_main definition final. 133 | public section. 134 | methods constructor. 135 | methods run_1_parallel. 136 | methods run_with_reducer. 137 | 138 | endclass. 139 | 140 | class lcl_main implementation. 141 | 142 | method constructor. 143 | * data(queue_tmp) = new zcl_thread_queue_dispatcher( i_threads = 2 ). 144 | endmethod. 145 | 146 | method run_1_parallel. 147 | 148 | write: / 'Running one task parallel'. 149 | 150 | data lo_task type ref to lcl_task. 151 | lo_task = lcl_task=>create( number_to_process = 1 ). 152 | lo_task->run_parallel( 'thread1' ). 153 | wait until lo_task->is_ready( ) = abap_true up to 10 seconds. 154 | 155 | if lo_task->has_error( ) = abap_true. 156 | write: / 'Error:' color = 6, lo_task->error( ). 157 | else. 158 | write: / 'Result:', lo_task->result( ). 159 | endif. 160 | 161 | endmethod. 162 | 163 | method run_with_reducer. 164 | 165 | write: / 'Running parallel with reducer'. 166 | 167 | data(lt_subjects) = value lcl_reducer=>tt_subject( " Payload 168 | for i = 1 then i + 1 while i <= 8 169 | ( number_to_process = i ) ). 170 | 171 | data(lo_reducer) = lcl_reducer=>create( 172 | iv_threads = 2 173 | it_subjects = lt_subjects ). 174 | 175 | lo_reducer->run_parallel( 'reducer' ). 176 | wait until lo_reducer->is_ready( ) = abap_true up to 10 seconds. 177 | 178 | loop at lo_reducer->result( ) assigning field-symbol(). 179 | if -error is not initial. 180 | write: / 'Error :' color 6, -task, -error. 181 | else. 182 | write: / 'Result:', -task, -result. 183 | endif. 184 | endloop. 185 | 186 | endmethod. 187 | 188 | endclass. 189 | 190 | form main. 191 | data(lo_app) = new lcl_main( ). 192 | 193 | write: / 'Started'. 194 | uline. 195 | lo_app->run_1_parallel( ). 196 | uline. 197 | lo_app->run_with_reducer( ). 198 | uline. 199 | write: / 'Finished'. 200 | endform. 201 | 202 | start-of-selection. 203 | perform main. 204 | -------------------------------------------------------------------------------- /src/zthread_runner_test_simple.prog.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | ZTHREAD_RUNNER_TEST_SIMPLE 7 | 1 8 | E 9 | X 10 | X 11 | 12 | 13 | 14 | R 15 | ZTHREAD_RUNNER_TEST_SIMPLE 16 | 26 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/zthreads.fugr.lzthreadstop.abap: -------------------------------------------------------------------------------- 1 | FUNCTION-POOL ZTHREADS. "MESSAGE-ID .. 2 | 3 | * INCLUDE LZTHREADSD... " Local class definition 4 | -------------------------------------------------------------------------------- /src/zthreads.fugr.lzthreadstop.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | LZTHREADSTOP 7 | S 8 | D$ 9 | I 10 | S 11 | X 12 | D$S 13 | X 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/zthreads.fugr.saplzthreads.abap: -------------------------------------------------------------------------------- 1 | ******************************************************************* 2 | * System-defined Include-files. * 3 | ******************************************************************* 4 | INCLUDE LZTHREADSTOP. " Global Data 5 | INCLUDE LZTHREADSUXX. " Function Modules 6 | 7 | ******************************************************************* 8 | * User-defined Include-files (if necessary). * 9 | ******************************************************************* 10 | * INCLUDE LZTHREADSF... " Subroutines 11 | * INCLUDE LZTHREADSO... " PBO-Modules 12 | * INCLUDE LZTHREADSI... " PAI-Modules 13 | * INCLUDE LZTHREADSE... " Events 14 | * INCLUDE LZTHREADSP... " Local class implement. 15 | * INCLUDE LZTHREADST99. " ABAP Unit tests 16 | -------------------------------------------------------------------------------- /src/zthreads.fugr.saplzthreads.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | SAPLZTHREADS 7 | S 8 | D$ 9 | F 10 | S 11 | E 12 | X 13 | D$S 14 | X 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/zthreads.fugr.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Threads test 6 | 7 | LZTHREADSTOP 8 | SAPLZTHREADS 9 | 10 | 11 | 12 | Z_THREAD_RUNNER 13 | R 14 | Abstract thread runner 15 | 16 | 17 | IV_RUNNER_CLASS_NAME 18 | STRING 19 | 20 | 21 | IV_RAW_PARAMS 22 | XSTRING 23 | 24 | 25 | 26 | 27 | EV_RAW_RESULT 28 | XSTRING 29 | 30 | 31 | 32 | 33 | ERROR 34 | 35 | 36 | 37 | 38 | IV_RUNNER_CLASS_NAME 39 | P 40 | 41 | 42 | IV_RAW_PARAMS 43 | P 44 | 45 | 46 | EV_RAW_RESULT 47 | P 48 | 49 | 50 | ERROR 51 | X 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/zthreads.fugr.z_thread_runner.abap: -------------------------------------------------------------------------------- 1 | FUNCTION Z_THREAD_RUNNER . 2 | *"---------------------------------------------------------------------- 3 | *"*"Local Interface: 4 | *" IMPORTING 5 | *" VALUE(IV_RUNNER_CLASS_NAME) TYPE STRING 6 | *" VALUE(IV_RAW_PARAMS) TYPE XSTRING 7 | *" EXPORTING 8 | *" VALUE(EV_RAW_RESULT) TYPE XSTRING 9 | *" EXCEPTIONS 10 | *" ERROR 11 | *"---------------------------------------------------------------------- 12 | 13 | data lo_runner type ref to object. 14 | data lx type ref to cx_root. 15 | data lv_text type c length 200. 16 | 17 | try. 18 | create object lo_runner type (iv_runner_class_name). 19 | call method lo_runner->('DESERIALIZE_STATE') exporting iv_xstr = iv_raw_params. 20 | call method lo_runner->('RUN'). 21 | call method lo_runner->('SERIALIZE_STATE') receiving rv_xstr = ev_raw_result. 22 | catch cx_root into lx. 23 | lv_text = lx->get_text( ). 24 | message s000(oo) raising error with 25 | lv_text+0(50) 26 | lv_text+50(50) 27 | lv_text+100(50) 28 | lv_text+150(50). 29 | endtry. 30 | 31 | ENDFUNCTION. 32 | --------------------------------------------------------------------------------