| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | /***********************************************************************************************************************************
 | 
					
						
							|  |  |  | Protocol Parallel Executor | 
					
						
							|  |  |  | ***********************************************************************************************************************************/ | 
					
						
							| 
									
										
										
										
											2019-04-26 08:08:23 -04:00
										 |  |  | #include "build.auto.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-16 13:39:58 -04:00
										 |  |  | #include <string.h>
 | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | #include <sys/select.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "common/debug.h"
 | 
					
						
							|  |  |  | #include "common/log.h"
 | 
					
						
							|  |  |  | #include "common/memContext.h"
 | 
					
						
							|  |  |  | #include "common/type/json.h"
 | 
					
						
							|  |  |  | #include "common/type/keyValue.h"
 | 
					
						
							|  |  |  | #include "common/type/list.h"
 | 
					
						
							| 
									
										
										
										
											2020-03-30 20:52:57 -04:00
										 |  |  | #include "common/type/object.h"
 | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | #include "protocol/command.h"
 | 
					
						
							|  |  |  | #include "protocol/parallel.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /***********************************************************************************************************************************
 | 
					
						
							|  |  |  | Object type | 
					
						
							|  |  |  | ***********************************************************************************************************************************/ | 
					
						
							|  |  |  | struct ProtocolParallel | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     MemContext *memContext; | 
					
						
							|  |  |  |     TimeMSec timeout;                                               // Max time to wait for jobs before returning
 | 
					
						
							| 
									
										
										
										
											2019-09-18 07:15:16 -04:00
										 |  |  |     ParallelJobCallback *callbackFunction;                          // Function to get new jobs
 | 
					
						
							|  |  |  |     void *callbackData;                                             // Data to pass to callback function
 | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     List *clientList;                                               // List of clients to process jobs
 | 
					
						
							|  |  |  |     List *jobList;                                                  // List of jobs to be processed
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ProtocolParallelJob **clientJobList;                            // Jobs being processing by each client
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ProtocolParallelJobState state;                                 // Overall state of job processing
 | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-03 18:52:54 -04:00
										 |  |  | OBJECT_DEFINE_FREE(PROTOCOL_PARALLEL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-03 18:01:28 -04:00
										 |  |  | /**********************************************************************************************************************************/ | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | ProtocolParallel * | 
					
						
							| 
									
										
										
										
											2019-09-18 07:15:16 -04:00
										 |  |  | protocolParallelNew(TimeMSec timeout, ParallelJobCallback *callbackFunction, void *callbackData) | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     FUNCTION_LOG_BEGIN(logLevelTrace); | 
					
						
							|  |  |  |         FUNCTION_LOG_PARAM(UINT64, timeout); | 
					
						
							| 
									
										
										
										
											2019-09-18 07:15:16 -04:00
										 |  |  |         FUNCTION_LOG_PARAM(FUNCTIONP, callbackFunction); | 
					
						
							|  |  |  |         FUNCTION_LOG_PARAM_P(VOID, callbackData); | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  |     FUNCTION_LOG_END(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-18 07:15:16 -04:00
										 |  |  |     ASSERT(callbackFunction != NULL); | 
					
						
							|  |  |  |     ASSERT(callbackData != NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  |     ProtocolParallel *this = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     MEM_CONTEXT_NEW_BEGIN("ProtocolParallel") | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         this = memNew(sizeof(ProtocolParallel)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-23 14:15:58 -07:00
										 |  |  |         *this = (ProtocolParallel) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             .memContext = MEM_CONTEXT_NEW(), | 
					
						
							|  |  |  |             .timeout = timeout, | 
					
						
							|  |  |  |             .callbackFunction = callbackFunction, | 
					
						
							|  |  |  |             .callbackData = callbackData, | 
					
						
							|  |  |  |             .clientList = lstNew(sizeof(ProtocolClient *)), | 
					
						
							|  |  |  |             .jobList = lstNew(sizeof(ProtocolParallelJob *)), | 
					
						
							|  |  |  |             .state = protocolParallelJobStatePending, | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     MEM_CONTEXT_NEW_END(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     FUNCTION_LOG_RETURN(PROTOCOL_PARALLEL, this); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-03 18:01:28 -04:00
										 |  |  | /**********************************************************************************************************************************/ | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | void | 
					
						
							|  |  |  | protocolParallelClientAdd(ProtocolParallel *this, ProtocolClient *client) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     FUNCTION_LOG_BEGIN(logLevelTrace); | 
					
						
							|  |  |  |         FUNCTION_LOG_PARAM(PROTOCOL_PARALLEL, this); | 
					
						
							|  |  |  |         FUNCTION_LOG_PARAM(PROTOCOL_CLIENT, client); | 
					
						
							|  |  |  |     FUNCTION_LOG_END(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ASSERT(this != NULL); | 
					
						
							|  |  |  |     ASSERT(client != NULL); | 
					
						
							|  |  |  |     ASSERT(this->state == protocolParallelJobStatePending); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (ioReadHandle(protocolClientIoRead(client)) == -1) | 
					
						
							|  |  |  |         THROW(AssertError, "client with read handle is required"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     lstAdd(this->clientList, &client); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     FUNCTION_LOG_RETURN_VOID(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-03 18:01:28 -04:00
										 |  |  | /**********************************************************************************************************************************/ | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | unsigned int | 
					
						
							|  |  |  | protocolParallelProcess(ProtocolParallel *this) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     FUNCTION_LOG_BEGIN(logLevelTrace); | 
					
						
							|  |  |  |         FUNCTION_LOG_PARAM(PROTOCOL_PARALLEL, this); | 
					
						
							|  |  |  |     FUNCTION_LOG_END(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ASSERT(this != NULL); | 
					
						
							|  |  |  |     ASSERT(this->state != protocolParallelJobStateDone); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     unsigned int result = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // If called for the first time, initialize processing
 | 
					
						
							|  |  |  |     if (this->state == protocolParallelJobStatePending) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         MEM_CONTEXT_BEGIN(this->memContext) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2020-01-23 14:15:58 -07:00
										 |  |  |             this->clientJobList = memNewPtrArray(lstSize(this->clientList)); | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         MEM_CONTEXT_END(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this->state = protocolParallelJobStateRunning; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Initialize the file descriptor set used for select
 | 
					
						
							|  |  |  |     fd_set selectSet; | 
					
						
							|  |  |  |     FD_ZERO(&selectSet); | 
					
						
							|  |  |  |     int handleMax = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Find clients that are running jobs
 | 
					
						
							|  |  |  |     unsigned int clientRunningTotal = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (unsigned int clientIdx = 0; clientIdx < lstSize(this->clientList); clientIdx++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (this->clientJobList[clientIdx] != NULL) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             int handle = ioReadHandle(protocolClientIoRead(*(ProtocolClient **)lstGet(this->clientList, clientIdx))); | 
					
						
							|  |  |  |             FD_SET((unsigned int)handle, &selectSet); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-13 18:05:54 -04:00
										 |  |  |             // Find the max file handle needed for select()
 | 
					
						
							|  |  |  |             MAX_ASSIGN(handleMax, handle); | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             clientRunningTotal++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // If clients are running then wait for one to finish
 | 
					
						
							|  |  |  |     if (clientRunningTotal > 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Initialize timeout struct used for select.  Recreate this structure each time since Linux (at least) will modify it.
 | 
					
						
							|  |  |  |         struct timeval timeoutSelect; | 
					
						
							|  |  |  |         timeoutSelect.tv_sec = (time_t)(this->timeout / MSEC_PER_SEC); | 
					
						
							|  |  |  |         timeoutSelect.tv_usec = (time_t)(this->timeout % MSEC_PER_SEC * 1000); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Determine if there is data to be read
 | 
					
						
							|  |  |  |         int completed = select(handleMax + 1, &selectSet, NULL, NULL, &timeoutSelect); | 
					
						
							|  |  |  |         THROW_ON_SYS_ERROR(completed == -1, AssertError, "unable to select from parallel client(s)"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // If any jobs have completed then get the results
 | 
					
						
							|  |  |  |         if (completed > 0) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             for (unsigned int clientIdx = 0; clientIdx < lstSize(this->clientList); clientIdx++) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 ProtocolParallelJob *job = this->clientJobList[clientIdx]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (job != NULL && | 
					
						
							|  |  |  |                     FD_ISSET( | 
					
						
							|  |  |  |                         (unsigned int)ioReadHandle(protocolClientIoRead(*(ProtocolClient **)lstGet(this->clientList, clientIdx))), | 
					
						
							|  |  |  |                         &selectSet)) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     MEM_CONTEXT_TEMP_BEGIN() | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         TRY_BEGIN() | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             protocolParallelJobResultSet( | 
					
						
							|  |  |  |                                 job, protocolClientReadOutput(*(ProtocolClient **)lstGet(this->clientList, clientIdx), true)); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         CATCH_ANY() | 
					
						
							|  |  |  |                         { | 
					
						
							| 
									
										
										
										
											2019-04-16 13:39:58 -04:00
										 |  |  |                             protocolParallelJobErrorSet(job, errorCode(), STR(errorMessage())); | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  |                         } | 
					
						
							|  |  |  |                         TRY_END(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         protocolParallelJobStateSet(job, protocolParallelJobStateDone); | 
					
						
							|  |  |  |                         this->clientJobList[clientIdx] = NULL; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     MEM_CONTEXT_TEMP_END(); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             result = (unsigned int)completed; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Find new jobs to be run
 | 
					
						
							|  |  |  |     for (unsigned int clientIdx = 0; clientIdx < lstSize(this->clientList); clientIdx++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // If nothing is running for this client
 | 
					
						
							|  |  |  |         if (this->clientJobList[clientIdx] == NULL) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2019-09-18 07:15:16 -04:00
										 |  |  |             // Get a new job
 | 
					
						
							|  |  |  |             ProtocolParallelJob *job = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             MEM_CONTEXT_BEGIN(lstMemContext(this->jobList)) | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2019-09-18 07:15:16 -04:00
										 |  |  |                 job = this->callbackFunction(this->callbackData, clientIdx); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             MEM_CONTEXT_END(); | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-18 07:15:16 -04:00
										 |  |  |             // If a new job was found
 | 
					
						
							|  |  |  |             if (job != NULL) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // Add to the job list
 | 
					
						
							|  |  |  |                 lstAdd(this->jobList, &job); | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-18 07:15:16 -04:00
										 |  |  |                 // Send the job to the client
 | 
					
						
							|  |  |  |                 protocolClientWriteCommand( | 
					
						
							|  |  |  |                     *(ProtocolClient **)lstGet(this->clientList, clientIdx), protocolParallelJobCommand(job)); | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-18 07:15:16 -04:00
										 |  |  |                 // Set client id and running state
 | 
					
						
							|  |  |  |                 protocolParallelJobProcessIdSet(job, clientIdx + 1); | 
					
						
							|  |  |  |                 protocolParallelJobStateSet(job, protocolParallelJobStateRunning); | 
					
						
							|  |  |  |                 this->clientJobList[clientIdx] = job; | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     FUNCTION_LOG_RETURN(UINT, result); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-03 18:01:28 -04:00
										 |  |  | /**********************************************************************************************************************************/ | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | ProtocolParallelJob * | 
					
						
							|  |  |  | protocolParallelResult(ProtocolParallel *this) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     FUNCTION_LOG_BEGIN(logLevelTrace); | 
					
						
							|  |  |  |         FUNCTION_LOG_PARAM(PROTOCOL_PARALLEL, this); | 
					
						
							|  |  |  |     FUNCTION_LOG_END(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ASSERT(this != NULL); | 
					
						
							|  |  |  |     ASSERT(this->state == protocolParallelJobStateRunning); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ProtocolParallelJob *result = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Find the next completed job
 | 
					
						
							|  |  |  |     for (unsigned int jobIdx = 0; jobIdx < lstSize(this->jobList); jobIdx++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         ProtocolParallelJob *job = *(ProtocolParallelJob **)lstGet(this->jobList, jobIdx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (protocolParallelJobState(job) == protocolParallelJobStateDone) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             result = protocolParallelJobMove(job, memContextCurrent()); | 
					
						
							| 
									
										
										
										
											2019-09-06 16:50:57 -04:00
										 |  |  |             lstRemoveIdx(this->jobList, jobIdx); | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // If all jobs have been returned then we are done
 | 
					
						
							|  |  |  |     if (lstSize(this->jobList) == 0) | 
					
						
							|  |  |  |         this->state = protocolParallelJobStateDone; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     FUNCTION_LOG_RETURN(PROTOCOL_PARALLEL_JOB, result); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-03 18:01:28 -04:00
										 |  |  | /**********************************************************************************************************************************/ | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | bool | 
					
						
							|  |  |  | protocolParallelDone(const ProtocolParallel *this) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     FUNCTION_LOG_BEGIN(logLevelTrace); | 
					
						
							|  |  |  |         FUNCTION_LOG_PARAM(PROTOCOL_PARALLEL, this); | 
					
						
							|  |  |  |     FUNCTION_LOG_END(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     FUNCTION_LOG_RETURN(BOOL, this->state == protocolParallelJobStateDone); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-03 18:01:28 -04:00
										 |  |  | /**********************************************************************************************************************************/ | 
					
						
							| 
									
										
										
										
											2019-02-27 21:10:52 +02:00
										 |  |  | String * | 
					
						
							|  |  |  | protocolParallelToLog(const ProtocolParallel *this) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return strNewFmt( | 
					
						
							|  |  |  |         "{state: %s, clientTotal: %u, jobTotal: %u}", protocolParallelJobToConstZ(this->state), lstSize(this->clientList), | 
					
						
							|  |  |  |         lstSize(this->jobList)); | 
					
						
							|  |  |  | } |