/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ /* * Copyright (c) 2016-2020 Intel, Inc. All rights reserved. * Copyright (c) 2021 Nanook Consulting. All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow * * $HEADER$ * */ #include #include #include #include #include "examples.h" #include static pmix_proc_t myproc; static void notification_fn(size_t evhdlr_registration_id, pmix_status_t status, const pmix_proc_t *source, pmix_info_t info[], size_t ninfo, pmix_info_t results[], size_t nresults, pmix_event_notification_cbfunc_fn_t cbfunc, void *cbdata) { myrel_t *lock = NULL; size_t n; pmix_status_t jobstatus = 0; pmix_proc_t affected; char *msg = NULL; memset(&affected, 0, sizeof(pmix_proc_t)); /* we should always have info returned to us - if not, there is * nothing we can do */ if (NULL != info) { for (n = 0; n < ninfo; n++) { if (0 == strncmp(info[n].key, PMIX_JOB_TERM_STATUS, PMIX_MAX_KEYLEN)) { jobstatus = info[n].value.data.status; } else if (0 == strncmp(info[n].key, PMIX_EVENT_AFFECTED_PROC, PMIX_MAX_KEYLEN)) { memcpy(&affected, info[n].value.data.proc, sizeof(pmix_proc_t)); } else if (0 == strncmp(info[n].key, PMIX_EVENT_RETURN_OBJECT, PMIX_MAX_KEYLEN)) { lock = (myrel_t *) info[n].value.data.ptr; } else if (0 == strncmp(info[n].key, PMIX_EVENT_TEXT_MESSAGE, PMIX_MAX_KEYLEN)) { msg = info[n].value.data.string; } } } if (NULL == lock) { fprintf(stderr, "LOCK WAS NOT RETURNED IN EVENT NOTIFICATION\n"); goto done; } /* save the status */ lock->lock.status = jobstatus; if (NULL != msg) { lock->nspace = strdup(msg); } /* release the lock */ DEBUG_WAKEUP_THREAD(&lock->lock); done: /* we _always_ have to execute the evhandler callback or * else the event progress engine will hang */ if (NULL != cbfunc) { cbfunc(PMIX_SUCCESS, NULL, 0, NULL, NULL, cbdata); } } /* event handler registration is done asynchronously because it * may involve the PMIx server registering with the host RM for * external events. So we provide a callback function that returns * the status of the request (success or an error), plus a numerical index * to the registered event. The index is used later on to deregister * an event handler - if we don't explicitly deregister it, then the * PMIx server will do so when it see us exit */ static void evhandler_reg_callbk(pmix_status_t status, size_t evhandler_ref, void *cbdata) { mylock_t *lock = (mylock_t *) cbdata; if (PMIX_SUCCESS != status) { fprintf(stderr, "Client %s:%d EVENT HANDLER REGISTRATION FAILED WITH STATUS %d, ref=%lu\n", myproc.nspace, myproc.rank, status, (unsigned long) evhandler_ref); } lock->status = status; lock->evhandler_ref = evhandler_ref; DEBUG_WAKEUP_THREAD(lock); } int main(int argc, char **argv) { pmix_status_t rc; pmix_info_t info; pmix_app_t *app; size_t ninfo, napps; bool flag; myrel_t myrel; mylock_t mylock; pmix_status_t code[6] = {PMIX_ERR_PROC_ABORTING, PMIX_ERR_PROC_ABORTED, PMIX_ERR_PROC_REQUESTED_ABORT, PMIX_EVENT_JOB_END, PMIX_ERR_UNREACH, PMIX_ERR_LOST_CONNECTION}; pmix_nspace_t appspace; /* we need to attach to a "system" PMIx server so we * can ask it to spawn applications for us. There can * only be one such connection on a node, so we will * instruct the tool library to only look for it */ flag = true; PMIX_INFO_LOAD(&info, PMIX_CONNECT_TO_SYSTEM, &flag, PMIX_BOOL); /* initialize the library and make the connection */ if (PMIX_SUCCESS != (rc = PMIx_tool_init(&myproc, &info, 1))) { fprintf(stderr, "PMIx_tool_init failed: %d\n", rc); exit(rc); } DEBUG_CONSTRUCT_MYREL(&myrel); /* register an event handler so we can be notified when * our spawned job completes, or if it fails (even at launch) */ DEBUG_CONSTRUCT_LOCK(&mylock); PMIX_INFO_LOAD(&info, PMIX_EVENT_RETURN_OBJECT, &myrel, PMIX_POINTER); PMIx_Register_event_handler(code, 6, &info, 1, notification_fn, evhandler_reg_callbk, (void *) &mylock); DEBUG_WAIT_THREAD(&mylock); rc = mylock.status; DEBUG_DESTRUCT_LOCK(&mylock); if (PMIX_SUCCESS != rc) { fprintf(stderr, "[%s:%d] Default handler registration failed\n", myproc.nspace, myproc.rank); goto done; } /* parse the cmd line and create our array of app structs * describing the application we want launched */ napps = 1; PMIX_APP_CREATE(app, napps); /* setup the executable */ app[0].cmd = strdup("app"); app[0].argv = (char **) malloc(2 * sizeof(char *)); app[0].argv[0] = strdup("app"); app[0].argv[1] = NULL; app[0].maxprocs = 128; /* can also provide environmental params in the app.env field */ /* provide directives so the apps do what the user requested - just * some random examples provided here*/ app[0].ninfo = 2; PMIX_INFO_CREATE(app[0].info, app[0].ninfo); PMIX_INFO_LOAD(&app[0].info[0], PMIX_MAPBY, "slot", PMIX_STRING); /* include a directive that we be notified upon completion of the job */ PMIX_INFO_LOAD(&app[0].info[1], PMIX_NOTIFY_COMPLETION, &flag, PMIX_BOOL); /* spawn the application */ PMIx_Spawn(NULL, 0, app, napps, appspace); /* cleanup */ PMIX_APP_FREE(app, napps); DEBUG_WAIT_THREAD(&myrel.lock); DEBUG_DESTRUCT_MYREL(&myrel); done: PMIx_tool_finalize(); return (0); }