|
| 1 | +! Author: Philipp Engel |
| 2 | +! Licence: ISC |
| 3 | +module dm_ipc_thread |
| 4 | + !! Abstraction layer over NNG threads. |
| 5 | + !! |
| 6 | + !! Thread routines must have the `bind(c)` attribute: |
| 7 | + !! |
| 8 | + !! ```fortran |
| 9 | + !! subroutine thread_callback(arg) bind(c) |
| 10 | + !! !! C-interoperable NNG thread routine. |
| 11 | + !! use, intrinsic :: iso_c_binding |
| 12 | + !! type(c_ptr), intent(in), value :: arg !! C pointer to client data. |
| 13 | + !! integer, pointer :: i !! Fortran pointer to client data. |
| 14 | + !! |
| 15 | + !! if (.not. c_associated(arg)) return |
| 16 | + !! call c_f_pointer(arg, i) |
| 17 | + !! print '("value: ", i0)', i |
| 18 | + !! end subroutine thread_callback |
| 19 | + !! ``` |
| 20 | + !! |
| 21 | + !! The dummy argument `arg` can be of any type. The thread routine and the |
| 22 | + !! argument have to be passed to the create function: |
| 23 | + !! |
| 24 | + !! ```fortran |
| 25 | + !! integer, target :: arg |
| 26 | + !! integer :: rc |
| 27 | + !! type(ipc_thread_type) :: thread |
| 28 | + !! |
| 29 | + !! arg = 123 |
| 30 | + !! |
| 31 | + !! rc = dm_ipc_thread_create(thread, thread_callback, arg) |
| 32 | + !! call dm_ipc_thread_join(thread) |
| 33 | + !! ``` |
| 34 | + !! |
| 35 | + !! In contrast to the POSIX thread interface of module `dm_posix_thread`, |
| 36 | + !! the NNG routines are cross-platform. |
| 37 | + use :: dm_c |
| 38 | + use :: dm_error |
| 39 | + use :: dm_kind |
| 40 | + implicit none (type, external) |
| 41 | + private |
| 42 | + |
| 43 | + abstract interface |
| 44 | + subroutine dm_ipc_thread_callback(ptr) bind(c) |
| 45 | + !! C-interoperable NNG thread routine. |
| 46 | + import :: c_ptr |
| 47 | + implicit none |
| 48 | + type(c_ptr), intent(in), value :: ptr !! Thread argument. |
| 49 | + end subroutine dm_ipc_thread_callback |
| 50 | + end interface |
| 51 | + |
| 52 | + type, public :: ipc_thread_type |
| 53 | + !! Opaque NNG thread type. |
| 54 | + private |
| 55 | + type(c_ptr) :: ctx = c_null_ptr !! NNG thread context. |
| 56 | + end type ipc_thread_type |
| 57 | + |
| 58 | + public :: dm_ipc_thread_create |
| 59 | + public :: dm_ipc_thread_join |
| 60 | +contains |
| 61 | + ! ************************************************************************** |
| 62 | + ! PUBLIC PROCEDURES. |
| 63 | + ! ************************************************************************** |
| 64 | + integer function dm_ipc_thread_create(thread, callback, argument) result(rc) |
| 65 | + !! The function creates a single thread of execution, running func with |
| 66 | + !! the argument arg. The thread is started immediately. A pointer to |
| 67 | + !! the NNG thread object is returned in dummy argument `thread`. |
| 68 | + !! |
| 69 | + !! The intention of this module is to facilitate writing parallel |
| 70 | + !! programs. Threads created by this module will be based upon the |
| 71 | + !! underlying threading mechanism of the system that NNG is running on. |
| 72 | + !! This may include use of coroutines. |
| 73 | + !! |
| 74 | + !! Using threads created by this function can make it easy to write |
| 75 | + !! programs that use simple sequential execution, using functions in |
| 76 | + !! the NNG suite that would otherwise normally wait synchronously for |
| 77 | + !! completion. |
| 78 | + !! |
| 79 | + !! When the thread is no longer needed, the `dm_ipc_thread_join()` |
| 80 | + !! routine should be used to reap it. (This function will block waiting |
| 81 | + !! for `callback` to return.) |
| 82 | + use :: nng, only: nng_thread_create |
| 83 | + use :: dm_ipc, only: dm_ipc_error |
| 84 | + |
| 85 | + type(ipc_thread_type), intent(out) :: thread !! IPC thread. |
| 86 | + procedure(dm_ipc_thread_callback) :: callback !! Thread routine. |
| 87 | + type(*), target, intent(inout) :: argument !! Client data to be passed to thread procedure. |
| 88 | + |
| 89 | + integer :: stat |
| 90 | + |
| 91 | + stat = nng_thread_create(thread%ctx, c_funloc(callback), c_loc(argument)) |
| 92 | + rc = dm_ipc_error(stat) |
| 93 | + end function dm_ipc_thread_create |
| 94 | + |
| 95 | + subroutine dm_ipc_thread_join(thread) |
| 96 | + !! Joins and destroys NNG thread. This routine is blocking. |
| 97 | + use :: nng, only: nng_thread_destroy |
| 98 | + |
| 99 | + type(ipc_thread_type), intent(inout) :: thread !! IPC thread. |
| 100 | + |
| 101 | + if (.not. c_associated(thread%ctx)) return |
| 102 | + call nng_thread_destroy(thread%ctx) |
| 103 | + end subroutine dm_ipc_thread_join |
| 104 | +end module dm_ipc_thread |
0 commit comments