Init commit of jobs output/error message peek project.

This commit is contained in:
Lei Chen 2026-05-09 16:57:54 +08:00
parent c772d7e601
commit d2b47e3985
38 changed files with 2267 additions and 21 deletions

View file

@ -673,3 +673,33 @@ int xcecli_jobinfo(xcecli_ctx_t c, uint64_t jobid, xce_job_t** job)
rc = xce_jobinfo(&(ctx->ctx), jobid, job);
return rc;
}
int xcecli_reply_fetchfile(xcecli_ctx_t c, xce_request_t* request,
xce_fetchfile_reply_t* freply)
{
api_ctx_t* ctx = (api_ctx_t*)c;
xce_reply_t reply;
if ((ctx == NULL) || (request == NULL) || (freply == NULL)) {
return -EINVAL;
}
init_reply_by_req(&reply, request);
reply.status = freply->error;
reply.data_which = RequestReply_data_fetchfilereply;
reply.data.fetchfilereply = freply;
return xce_reply(&(ctx->ep), &(ctx->ctx.myinfo), &reply);
}
int xcecli_fetchfile(xcecli_ctx_t c, xce_fetchfile_t* msg,
xce_fetchfile_reply_t** reply)
{
api_ctx_t* ctx = (api_ctx_t*)c;
if ((ctx == NULL) || (msg == NULL) || (reply == NULL)) {
return -EINVAL;
}
return xce_fetchfile(&(ctx->ctx), msg, reply);
}

View file

@ -147,6 +147,8 @@ char* xce_request_op_name(xce_request_t* req)
return "setpreason";
case OPCode_clrpreason:
return "clrpreason";
case OPCode_fetchfile:
return "fetchfile";
default:
break;
}

View file

@ -163,6 +163,7 @@ libxce_test_src = ['testsuite/test.c',
'testsuite/test_wobj.c',
'testsuite/test_qobj.c',
'testsuite/test_request.c',
'testsuite/test_cbuf.c',
]
libxce_test_deps = [libxce_deps, librtti_dep, unity_dep, libxce_dep]

View file

@ -187,6 +187,32 @@ int cbuf_tryget(cbuf_t* b, void* it)
return rc;
}
int cbuf_timedget(cbuf_t* b, void* it, const struct timespec* deadline)
{
if ((b == NULL) || (it == NULL) || (deadline == NULL)) {
return -EINVAL;
}
pthread_mutex_lock(&(b->mutex));
while (b->len == 0) {
int rc = pthread_cond_timedwait(&(b->condget), &(b->mutex), deadline);
if (rc == ETIMEDOUT) {
pthread_mutex_unlock(&(b->mutex));
return -ETIMEDOUT;
}
}
memcpy(it, (void*)((char*)b->data) + b->isize * b->head, b->isize);
b->head = (b->head + 1) % b->ncap;
b->len--;
pthread_cond_signal(&(b->condput));
pthread_mutex_unlock(&(b->mutex));
return 0;
}
int my_rwlock_init(my_rwlock_t* l)
{
if (l == NULL) {

View file

@ -935,3 +935,26 @@ int xce_clear_reason_req(endpoint_t* ep, xce_clear_reason_t* reason)
return xce_request(ep, &request);
}
int xce_fetchfile_req(endpoint_t* ep, xce_fetchfile_t* msg)
{
/* clang-format off */
xce_request_t request = {
.opcode = OPCode_fetchfile,
.body_which = Request_body_fetchfilemsg,
.body = {
.fetchfilemsg = msg
}
};
/* clang-format on */
if ((ep == NULL) || (msg == NULL)) {
return -EINVAL;
}
if (msg->filepath == NULL || msg->filepath[0] == '\0') {
return -EINVAL;
}
return xce_request(ep, &request);
}

View file

@ -29,6 +29,7 @@ extern void test_jobobj(void);
extern void test_qobj(void);
extern void test_wobj(void);
extern void test_request(void);
extern void test_cbuf(void);
void setUp(void) { return; }
@ -127,6 +128,7 @@ int main(int argc, char* argv[])
TEST(test_wobj);
TEST(test_qobj);
TEST(test_request);
TEST(test_cbuf);
return UNITY_END();
}

View file

@ -0,0 +1,207 @@
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <time.h>
#include <unity/unity.h>
#include "mylock.h"
/* ------------------------------------------------------------------ *
* helpers
* ------------------------------------------------------------------ */
static void make_deadline(struct timespec *ts, long ms)
{
clock_gettime(CLOCK_REALTIME, ts);
ts->tv_sec += ms / 1000;
ts->tv_nsec += (ms % 1000) * 1000000L;
if (ts->tv_nsec >= 1000000000L) {
ts->tv_sec++;
ts->tv_nsec -= 1000000000L;
}
}
/* ------------------------------------------------------------------ *
* cbuf_timedget: argument validation
* ------------------------------------------------------------------ */
static void test_cbuf_timedget_null_buf(void)
{
int val;
struct timespec ts;
make_deadline(&ts, 100);
TEST_ASSERT_EQUAL_INT(-EINVAL, cbuf_timedget(NULL, &val, &ts));
}
static void test_cbuf_timedget_null_item(void)
{
cbuf_t b;
cbuf_init(&b, 4, sizeof(int));
struct timespec ts;
make_deadline(&ts, 100);
TEST_ASSERT_EQUAL_INT(-EINVAL, cbuf_timedget(&b, NULL, &ts));
cbuf_fini(&b);
}
static void test_cbuf_timedget_null_deadline(void)
{
cbuf_t b;
cbuf_init(&b, 4, sizeof(int));
int val;
TEST_ASSERT_EQUAL_INT(-EINVAL, cbuf_timedget(&b, &val, NULL));
cbuf_fini(&b);
}
/* ------------------------------------------------------------------ *
* cbuf_timedget: timeout on empty buffer
* ------------------------------------------------------------------ */
static void test_cbuf_timedget_timeout_empty(void)
{
cbuf_t b;
cbuf_init(&b, 4, sizeof(int));
int val = 0;
struct timespec ts;
make_deadline(&ts, 50); /* 50 ms */
int rc = cbuf_timedget(&b, &val, &ts);
TEST_ASSERT_EQUAL_INT(-ETIMEDOUT, rc);
cbuf_fini(&b);
}
/* ------------------------------------------------------------------ *
* cbuf_timedget: normal read when item is already available
* ------------------------------------------------------------------ */
static void test_cbuf_timedget_success_immediate(void)
{
cbuf_t b;
cbuf_init(&b, 4, sizeof(int));
int put_val = 42;
cbuf_put(&b, &put_val);
int got_val = 0;
struct timespec ts;
make_deadline(&ts, 1000); /* generous 1 s */
int rc = cbuf_timedget(&b, &got_val, &ts);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_INT(42, got_val);
cbuf_fini(&b);
}
/* ------------------------------------------------------------------ *
* cbuf_timedget: producer thread scenario
* ------------------------------------------------------------------ */
typedef struct {
cbuf_t *buf;
int value;
long delay_ms;
} producer_arg_t;
static void *producer_thread(void *arg)
{
producer_arg_t *a = (producer_arg_t *)arg;
struct timespec ts = { .tv_sec = a->delay_ms / 1000,
.tv_nsec = (a->delay_ms % 1000) * 1000000L };
nanosleep(&ts, NULL);
cbuf_put(a->buf, &a->value);
return NULL;
}
static void test_cbuf_timedget_success_after_producer(void)
{
cbuf_t b;
cbuf_init(&b, 4, sizeof(int));
producer_arg_t arg = { .buf = &b, .value = 99, .delay_ms = 30 };
pthread_t tid;
pthread_create(&tid, NULL, producer_thread, &arg);
int got_val = 0;
struct timespec ts;
make_deadline(&ts, 500); /* 500 ms — producer fires at 30 ms */
int rc = cbuf_timedget(&b, &got_val, &ts);
pthread_join(tid, NULL);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_INT(99, got_val);
cbuf_fini(&b);
}
/* ------------------------------------------------------------------ *
* cbuf_timedget: timeout beats slow producer
* ------------------------------------------------------------------ */
static void test_cbuf_timedget_timeout_beats_slow_producer(void)
{
cbuf_t b;
cbuf_init(&b, 4, sizeof(int));
/* Producer fires at 200 ms, but we timeout at 50 ms */
producer_arg_t arg = { .buf = &b, .value = 7, .delay_ms = 200 };
pthread_t tid;
pthread_create(&tid, NULL, producer_thread, &arg);
int got_val = 0;
struct timespec ts;
make_deadline(&ts, 50);
int rc = cbuf_timedget(&b, &got_val, &ts);
TEST_ASSERT_EQUAL_INT(-ETIMEDOUT, rc);
pthread_join(tid, NULL);
cbuf_fini(&b);
}
/* ------------------------------------------------------------------ *
* cbuf_tryget: basic tests (also not previously declared in header)
* ------------------------------------------------------------------ */
static void test_cbuf_tryget_empty(void)
{
cbuf_t b;
cbuf_init(&b, 4, sizeof(int));
int val;
TEST_ASSERT_EQUAL_INT(-EAGAIN, cbuf_tryget(&b, &val));
cbuf_fini(&b);
}
static void test_cbuf_tryget_success(void)
{
cbuf_t b;
cbuf_init(&b, 4, sizeof(int));
int put_val = 77;
cbuf_put(&b, &put_val);
int got_val = 0;
TEST_ASSERT_EQUAL_INT(0, cbuf_tryget(&b, &got_val));
TEST_ASSERT_EQUAL_INT(77, got_val);
cbuf_fini(&b);
}
/* ------------------------------------------------------------------ *
* suite entry point
* ------------------------------------------------------------------ */
void test_cbuf(void)
{
printf("\n>> cbuf_timedget / cbuf_tryget tests\n\n");
RUN_TEST(test_cbuf_timedget_null_buf);
RUN_TEST(test_cbuf_timedget_null_item);
RUN_TEST(test_cbuf_timedget_null_deadline);
RUN_TEST(test_cbuf_timedget_timeout_empty);
RUN_TEST(test_cbuf_timedget_success_immediate);
RUN_TEST(test_cbuf_timedget_success_after_producer);
RUN_TEST(test_cbuf_timedget_timeout_beats_slow_producer);
RUN_TEST(test_cbuf_tryget_empty);
RUN_TEST(test_cbuf_tryget_success);
}

View file

@ -1,8 +1,17 @@
#include <errno.h>
#include <time.h>
#include "mylock.h"
#include "utils.h"
#include "xceapi.h"
/* All synchronous API calls wait at most 10 seconds for a reply. */
static void cbuf_deadline_10s(struct timespec* ts)
{
clock_gettime(CLOCK_REALTIME, ts);
ts->tv_sec += 10;
}
int xce_hello(xce_ctx_t* ctx, char* msg)
{
int rc, status;
@ -19,7 +28,8 @@ int xce_hello(xce_ctx_t* ctx, char* msg)
if (rc < 0) {
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -113,7 +123,8 @@ int xce_get_with_condition(xce_ctx_t* ctx, xce_get_t* get, void*** data)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -192,7 +203,8 @@ int xce_resval(xce_ctx_t* ctx, char* host, char* res, xce_resval_t*** vals)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -225,7 +237,8 @@ int xce_join(xce_ctx_t* ctx, int role, hostattrs_t* attrs)
if (rc < 0) {
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -259,7 +272,8 @@ int xce_jobdispatch(xce_ctx_t* ctx, xce_job_dispatch_t* info)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -301,7 +315,8 @@ int xce_jobsubmit(xce_ctx_t* ctx, xce_job_submit_t* submit, uint64_t* jobid)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -414,7 +429,8 @@ int xce_clusterconf(xce_ctx_t* ctx, xce_clusterconf_t** conf)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -458,7 +474,8 @@ int xce_update_resvals(xce_ctx_t* ctx, int count, xce_resval_t** vals)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -501,7 +518,8 @@ int xce_update_jusage(xce_ctx_t* ctx, uint64_t jobid, int npids, int* pids,
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -527,7 +545,8 @@ int xce_jobctrl(xce_ctx_t* ctx, xce_jobctrl_t* ctrl)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -553,7 +572,8 @@ int xce_queuectrl(xce_ctx_t* ctx, xce_queuectrl_t* ctrl)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -579,7 +599,8 @@ int xce_hostctrl(xce_ctx_t* ctx, xce_hostctrl_t* ctrl)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -605,7 +626,8 @@ int xce_runtask(xce_ctx_t* ctx, xce_taskrun_t* runinfo)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -631,7 +653,8 @@ int xce_set_pend_reason(xce_ctx_t* ctx, xce_preason_set_t* reasons)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -657,7 +680,8 @@ int xce_clear_reason(xce_ctx_t* ctx, xce_clear_reason_t* reasons)
return rc;
}
rc = cbuf_get(&(ctx->wbuf), &status);
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
@ -697,3 +721,37 @@ int xce_jobinfo(xce_ctx_t* ctx, uint64_t jobid, xce_job_t** job)
return 1;
}
int xce_fetchfile(xce_ctx_t* ctx, xce_fetchfile_t* msg, xce_fetchfile_reply_t** reply)
{
int rc;
int status;
if ((ctx == NULL) || (msg == NULL) || (reply == NULL)) {
return -EINVAL;
}
if (ctx->state != XCE_CTX_RUN) {
return -EINVAL;
}
ctx->reply.u.fetchfilereply = NULL;
rc = xce_fetchfile_req(ctx->ep, msg);
if (rc < 0) {
return rc;
}
struct timespec _dl; cbuf_deadline_10s(&_dl);
rc = cbuf_timedget(&(ctx->wbuf), &status, &_dl);
if (rc < 0) {
return rc;
}
if (status < 0) {
return status;
}
MOVE_PTR(*reply, ctx->reply.u.fetchfilereply);
return 0;
}

View file

@ -49,6 +49,13 @@ static int handle_reply(xce_ctx_t* ctx, xce_reply_t* reply)
rc = 1;
}
break;
case OPCode_fetchfile:
rc = -1;
if (reply->data_which == RequestReply_data_fetchfilereply) {
MOVE_PTR(ctx->reply.u.fetchfilereply, reply->data.fetchfilereply);
rc = 1;
}
break;
default:
rc = -ENOENT;
break;