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"
|
2021-04-08 10:04:57 -04:00
|
|
|
#include "common/macro.h"
|
2019-02-27 21:10:52 +02:00
|
|
|
#include "common/type/keyValue.h"
|
|
|
|
#include "common/type/list.h"
|
|
|
|
#include "protocol/command.h"
|
2020-07-28 12:15:33 -04:00
|
|
|
#include "protocol/helper.h"
|
2019-02-27 21:10:52 +02:00
|
|
|
#include "protocol/parallel.h"
|
|
|
|
|
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Object type
|
|
|
|
***********************************************************************************************************************************/
|
|
|
|
struct ProtocolParallel
|
|
|
|
{
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2023-01-02 15:24:51 +07:00
|
|
|
FN_EXTERN 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);
|
|
|
|
|
2022-05-18 10:52:01 -04:00
|
|
|
OBJ_NEW_BEGIN(ProtocolParallel, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
|
2019-02-27 21:10:52 +02:00
|
|
|
{
|
2020-01-23 14:15:58 -07:00
|
|
|
*this = (ProtocolParallel)
|
|
|
|
{
|
|
|
|
.timeout = timeout,
|
|
|
|
.callbackFunction = callbackFunction,
|
|
|
|
.callbackData = callbackData,
|
2020-07-20 15:22:33 -04:00
|
|
|
.clientList = lstNewP(sizeof(ProtocolClient *)),
|
|
|
|
.jobList = lstNewP(sizeof(ProtocolParallelJob *)),
|
2020-01-23 14:15:58 -07:00
|
|
|
.state = protocolParallelJobStatePending,
|
|
|
|
};
|
2019-02-27 21:10:52 +02:00
|
|
|
}
|
2021-09-01 11:10:35 -04:00
|
|
|
OBJ_NEW_END();
|
2019-02-27 21:10:52 +02:00
|
|
|
|
|
|
|
FUNCTION_LOG_RETURN(PROTOCOL_PARALLEL, this);
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2023-01-02 15:24:51 +07:00
|
|
|
FN_EXTERN void
|
2019-02-27 21:10:52 +02:00
|
|
|
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);
|
|
|
|
|
2021-06-24 13:31:16 -04:00
|
|
|
if (protocolClientIoReadFd(client) == -1)
|
2020-08-05 18:25:07 -04:00
|
|
|
THROW(AssertError, "client with read fd is required");
|
2019-02-27 21:10:52 +02:00
|
|
|
|
|
|
|
lstAdd(this->clientList, &client);
|
|
|
|
|
|
|
|
FUNCTION_LOG_RETURN_VOID();
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2023-01-02 15:24:51 +07:00
|
|
|
FN_EXTERN unsigned int
|
2019-02-27 21:10:52 +02:00
|
|
|
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;
|
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
MEM_CONTEXT_TEMP_BEGIN()
|
2019-02-27 21:10:52 +02:00
|
|
|
{
|
2021-08-18 08:18:11 -04:00
|
|
|
// If called for the first time, initialize processing
|
|
|
|
if (this->state == protocolParallelJobStatePending)
|
2019-02-27 21:10:52 +02:00
|
|
|
{
|
2022-04-25 09:12:25 -04:00
|
|
|
MEM_CONTEXT_OBJ_BEGIN(this)
|
2021-08-18 08:18:11 -04:00
|
|
|
{
|
|
|
|
this->clientJobList = memNewPtrArray(lstSize(this->clientList));
|
|
|
|
}
|
2022-04-25 09:12:25 -04:00
|
|
|
MEM_CONTEXT_OBJ_END();
|
2019-02-27 21:10:52 +02:00
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
this->state = protocolParallelJobStateRunning;
|
|
|
|
}
|
2019-02-27 21:10:52 +02:00
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
// Initialize the file descriptor set used for select
|
|
|
|
fd_set selectSet;
|
|
|
|
FD_ZERO(&selectSet);
|
|
|
|
int fdMax = -1;
|
2019-02-27 21:10:52 +02:00
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
// Find clients that are running jobs
|
|
|
|
unsigned int clientRunningTotal = 0;
|
2019-02-27 21:10:52 +02:00
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
for (unsigned int clientIdx = 0; clientIdx < lstSize(this->clientList); clientIdx++)
|
2019-02-27 21:10:52 +02:00
|
|
|
{
|
2021-08-18 08:18:11 -04:00
|
|
|
if (this->clientJobList[clientIdx] != NULL)
|
|
|
|
{
|
|
|
|
int fd = protocolClientIoReadFd(*(ProtocolClient **)lstGet(this->clientList, clientIdx));
|
|
|
|
FD_SET(fd, &selectSet);
|
2019-02-27 21:10:52 +02:00
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
// Find the max file descriptor needed for select()
|
|
|
|
MAX_ASSIGN(fdMax, fd);
|
2019-02-27 21:10:52 +02:00
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
clientRunningTotal++;
|
|
|
|
}
|
2019-02-27 21:10:52 +02:00
|
|
|
}
|
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
// 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 = (suseconds_t)(this->timeout % MSEC_PER_SEC * 1000);
|
2019-02-27 21:10:52 +02:00
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
// Determine if there is data to be read
|
|
|
|
int completed = select(fdMax + 1, &selectSet, NULL, NULL, &timeoutSelect);
|
|
|
|
THROW_ON_SYS_ERROR(completed == -1, AssertError, "unable to select from parallel client(s)");
|
2019-02-27 21:10:52 +02:00
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
// If any jobs have completed then get the results
|
|
|
|
if (completed > 0)
|
2019-02-27 21:10:52 +02:00
|
|
|
{
|
2021-08-18 08:18:11 -04:00
|
|
|
for (unsigned int clientIdx = 0; clientIdx < lstSize(this->clientList); clientIdx++)
|
2019-02-27 21:10:52 +02:00
|
|
|
{
|
2021-08-18 08:18:11 -04:00
|
|
|
ProtocolParallelJob *job = this->clientJobList[clientIdx];
|
2021-06-24 13:31:16 -04:00
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
if (job != NULL &&
|
|
|
|
FD_ISSET(
|
|
|
|
protocolClientIoReadFd(*(ProtocolClient **)lstGet(this->clientList, clientIdx)),
|
|
|
|
&selectSet))
|
|
|
|
{
|
|
|
|
MEM_CONTEXT_TEMP_BEGIN()
|
2019-02-27 21:10:52 +02:00
|
|
|
{
|
2021-08-18 08:18:11 -04:00
|
|
|
TRY_BEGIN()
|
|
|
|
{
|
|
|
|
ProtocolClient *const client = *(ProtocolClient **)lstGet(this->clientList, clientIdx);
|
|
|
|
|
|
|
|
protocolParallelJobResultSet(job, protocolClientDataGet(client));
|
|
|
|
protocolClientDataEndGet(client);
|
|
|
|
}
|
|
|
|
CATCH_ANY()
|
|
|
|
{
|
|
|
|
protocolParallelJobErrorSet(job, errorCode(), STR(errorMessage()));
|
|
|
|
}
|
|
|
|
TRY_END();
|
|
|
|
|
|
|
|
protocolParallelJobStateSet(job, protocolParallelJobStateDone);
|
|
|
|
this->clientJobList[clientIdx] = NULL;
|
2019-02-27 21:10:52 +02:00
|
|
|
}
|
2021-08-18 08:18:11 -04:00
|
|
|
MEM_CONTEXT_TEMP_END();
|
2019-02-27 21:10:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
result = (unsigned int)completed;
|
|
|
|
}
|
2019-02-27 21:10:52 +02:00
|
|
|
}
|
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
// Find new jobs to be run
|
|
|
|
for (unsigned int clientIdx = 0; clientIdx < lstSize(this->clientList); clientIdx++)
|
2019-02-27 21:10:52 +02:00
|
|
|
{
|
2021-08-18 08:18:11 -04:00
|
|
|
// If nothing is running for this client
|
|
|
|
if (this->clientJobList[clientIdx] == NULL)
|
2019-02-27 21:10:52 +02:00
|
|
|
{
|
2021-08-18 08:18:11 -04:00
|
|
|
// Get a new job
|
|
|
|
ProtocolParallelJob *job = NULL;
|
2019-02-27 21:10:52 +02:00
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
MEM_CONTEXT_BEGIN(lstMemContext(this->jobList))
|
|
|
|
{
|
|
|
|
job = this->callbackFunction(this->callbackData, clientIdx);
|
|
|
|
}
|
|
|
|
MEM_CONTEXT_END();
|
|
|
|
|
|
|
|
// 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
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
// Put command
|
|
|
|
protocolClientCommandPut(
|
2021-10-05 14:06:59 -04:00
|
|
|
*(ProtocolClient **)lstGet(this->clientList, clientIdx), protocolParallelJobCommand(job), false);
|
2019-02-27 21:10:52 +02:00
|
|
|
|
2021-08-18 08:18:11 -04:00
|
|
|
// Set client id and running state
|
|
|
|
protocolParallelJobProcessIdSet(job, clientIdx + 1);
|
|
|
|
protocolParallelJobStateSet(job, protocolParallelJobStateRunning);
|
|
|
|
this->clientJobList[clientIdx] = job;
|
|
|
|
}
|
|
|
|
// Else no more jobs for this client so free it
|
|
|
|
else
|
|
|
|
protocolLocalFree(clientIdx + 1);
|
2019-02-27 21:10:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-18 08:18:11 -04:00
|
|
|
MEM_CONTEXT_TEMP_END();
|
2019-02-27 21:10:52 +02:00
|
|
|
|
|
|
|
FUNCTION_LOG_RETURN(UINT, result);
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2023-01-02 15:24:51 +07:00
|
|
|
FN_EXTERN ProtocolParallelJob *
|
2019-02-27 21:10:52 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FUNCTION_LOG_RETURN(PROTOCOL_PARALLEL_JOB, result);
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2023-01-02 15:24:51 +07:00
|
|
|
FN_EXTERN bool
|
2020-09-02 11:03:49 -04:00
|
|
|
protocolParallelDone(ProtocolParallel *this)
|
2019-02-27 21:10:52 +02:00
|
|
|
{
|
|
|
|
FUNCTION_LOG_BEGIN(logLevelTrace);
|
|
|
|
FUNCTION_LOG_PARAM(PROTOCOL_PARALLEL, this);
|
|
|
|
FUNCTION_LOG_END();
|
|
|
|
|
2020-09-02 11:03:49 -04:00
|
|
|
ASSERT(this != NULL);
|
|
|
|
ASSERT(this->state != protocolParallelJobStatePending);
|
|
|
|
|
|
|
|
// If there are no jobs left then we are done
|
2021-01-29 14:27:56 -05:00
|
|
|
if (this->state != protocolParallelJobStateDone && lstEmpty(this->jobList))
|
2020-09-02 11:03:49 -04:00
|
|
|
this->state = protocolParallelJobStateDone;
|
|
|
|
|
2019-02-27 21:10:52 +02:00
|
|
|
FUNCTION_LOG_RETURN(BOOL, this->state == protocolParallelJobStateDone);
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2023-01-12 17:14:36 +07:00
|
|
|
FN_EXTERN void
|
|
|
|
protocolParallelToLog(const ProtocolParallel *const this, StringStatic *const debugLog)
|
2019-02-27 21:10:52 +02:00
|
|
|
{
|
2023-01-12 17:14:36 +07:00
|
|
|
strStcCat(debugLog, "{state: ");
|
|
|
|
strStcResultSizeInc(debugLog, strIdToLog(this->state, strStcRemains(debugLog), strStcRemainsSize(debugLog)));
|
|
|
|
strStcFmt(debugLog, ", clientTotal: %u, jobTotal: %u}", lstSize(this->clientList), lstSize(this->jobList));
|
2019-02-27 21:10:52 +02:00
|
|
|
}
|