To understand the working of the jobs engine I created an embryo test plugin which creates a job upon calling the REST API
/myapp/hello. In the callback OrthancPluginJobStep I increment a counter with a delay of 5 seconds. This callback returns SUCCESS
when the counter equals/greater 4. The test plugin works as expected and the job “Customization” can be paused, resumed and cancelled.
After succesful completion the job is shown with the correct duration of 4 steps x 5 seconds = 20 seconds.
The following figure shows the running, pending and inactive job and the general info about the completed job.
Nevertheless I don’t understand the relation between the generic pointer void *job and the opaque structure OrthancPluginJob.
What is the correct value to set to void *job? I point to the address of a string in the present plugin!
My second question is: When and where should I call the function OrthancPluginFreeJob?
The “job” opaque structure is a user-specific payload that allows your plugin to store all information about its jobs (beyond a simple statically-allocated string as in your case). In object-oriented programming, this is the equivalent of the “this” pointer in C++ or “self” in Python: https://en.wikipedia.org/wiki/This_(computer_programming)
The memory associated with “job” is allocated by your plugin (i.e. it is your plugin that calls “malloc” or “new” to create the “job” object), and its ownership is transferred to the Orthanc plugin engine by calling “OrthancPluginSubmitJob()”.
The callback “OrthancPluginFreeJob()” must invoke a function to release the memory occupied by this user-specific payload. In C (resp. C++), it would typically consist in calling “free” (resp. “delete”) on the object.
thank you for the provided details. I carefully reexamined all the indicated links. I think there are some contradictions between your answer and the description of the job related structures and functions in the Orthanc Plugin SDK, now version 1.5.7.
My current understanding is the following :
The generic pointer void * job points to a user-specific pay-load (in my case only a simple string, but it can be a structure specified by the user).
This pointer is provided to the function OrthancPluginCreateJob() of the module Toolbox in the Orthanc Plugin SDK. This function returns a pointer to the opaque structure OrthancPluginJob.
The pointer to the opaque structure OrthancPluginJob is provided to the function OrthancPluginSubmitJob() of the same module Toolbox which transfers the ownership to the Orthanc Plugin engine. The function returns an ID for the newly submitted job.
After submission of the OrthancPluginJob, it’s not necessary to free the job. The SDK documentation for the function OrthancPluginCreateJob() says : “It must be freed with OrthancPluginFreeJob(), as long as it is not submitted with OrthancPluginSubmitJob().”
The job ID string must be freed with the function OrthancPluginFreeString() in the public function OrthancPluginFinalize() during the Orthanc shutdown.
Is my understanding correct ?
best regards,
Marco
P.S. Today I also looked into the code of the new DICOMweb plugin, version 1.0. In the release news it’s specified that the WADO-RS and STOW-RS client now create Orthanc jobs, but I don’t find no reference to jobs in the related source code ?
Today I did some (successful) trials with the new DICOMweb plugin between two Orthanc servers 1.5.7.
Please ignore the P.S. in my previous message. In the meantime I found the job related code in the source code of the file DicomWebClient.cpp. I hope that the deep examination of this example will help me to fully understand the job engine working.
Sorry for the delay, and glad to read that you found the jobs-related code in release 1.0 of the DICOMweb plugin.
I think that your understanding is correct: I guess the contradiction you mention is the fact that my previous answer didn’t mention the call to “OrthancPluginCreateJob()”.
In your step (5), I would however recommend you to free the string as soon as “OrthancPluginSubmitJob()” exits, in order to avoid any memory leak in the Orthanc core. You can obviously copy this value to some variable of your plugin if you need it for further processing.
Also, during the call to “OrthancPluginCreateJob()”, make sure to set the “finalize” argument to a function that will free your string. I would highly advise you to use a tool such as valgrind to detect memory allocation problems.
Thank you again for your great assistance to the Orthanc users.
Exploring the source code of the DicomWeb plugin helped me to understand the working of the job engine. I took up your suggestion to use the C++ wrapper and to derive a class from the Orthanc Job class for my RadioLogicCreator plugin.
I tried also valgrind which was new to me. Great tool.
I submitted the RadioLogic project (a radiology case-based learning and self-asessment tool for the Orthanc ecosystem) to the upcoming first Orthanc conference in December this year.
In the meantime I will finetune the project and create a public Github repository to share the code with the community. Starting with a minimal job-submission plugin, I will present the different steps of my project in the related Github Wiki to assist other developers in the creation of Orthanc plugins.