From 68ffe5f09bcbfab1e7b0bbad0c6e6f4b8a69d71e Mon Sep 17 00:00:00 2001 From: Advait Tahilyani Date: Sat, 27 Sep 2025 14:17:08 -0500 Subject: [PATCH] Pup changes --- include/CMakeLists.txt | 14 + include/charm-api.h | 9 + include/conv-rdma.h | 30 + src/CMakeLists.txt | 16 +- src/conv-ccs/ccs-auth.C | 289 ++++++++ src/conv-ccs/ccs-auth.h | 47 ++ src/conv-ccs/ccs-builtins.C | 746 +++++++++++++++++++ src/conv-ccs/ccs-builtins.h | 41 ++ src/conv-ccs/ccs-client.C | 387 ++++++++++ src/conv-ccs/ccs-client.h | 77 ++ src/conv-ccs/ccs-server.C | 566 +++++++++++++++ src/conv-ccs/ccs-server.h | 111 +++ src/conv-ccs/conv-ccs.C | 599 ++++++++++++++++ src/conv-ccs/conv-ccs.h | 121 ++++ src/conv-ccs/middle-ccs.C | 221 ++++++ src/converse_config.h.in | 18 + src/pup/pup.h | 1275 +++++++++++++++++++++++++++++++++ src/pup/pup_c.C | 204 ++++++ src/pup/pup_c.h | 118 +++ src/pup/pup_c_functions.h | 78 ++ src/pup/pup_cmialloc.C | 88 +++ src/pup/pup_cmialloc.h | 144 ++++ src/pup/pup_f.f90.sh | 501 +++++++++++++ src/pup/pup_mpi.h | 68 ++ src/pup/pup_paged.C | 141 ++++ src/pup/pup_paged.h | 84 +++ src/pup/pup_stl.h | 701 ++++++++++++++++++ src/pup/pup_toNetwork.C | 145 ++++ src/pup/pup_toNetwork.h | 132 ++++ src/pup/pup_toNetwork4.C | 97 +++ src/pup/pup_toNetwork4.h | 78 ++ src/pup/pup_util.C | 1121 +++++++++++++++++++++++++++++ src/pup/pup_xlater.C | 324 +++++++++ src/pup/pupf.h | 52 ++ tests/CMakeLists.txt | 1 + tests/pup_test/CMakeLists.txt | 2 + tests/pup_test/pup_test.cpp | 237 ++++++ 37 files changed, 8882 insertions(+), 1 deletion(-) create mode 100644 include/charm-api.h create mode 100644 src/conv-ccs/ccs-auth.C create mode 100644 src/conv-ccs/ccs-auth.h create mode 100644 src/conv-ccs/ccs-builtins.C create mode 100644 src/conv-ccs/ccs-builtins.h create mode 100644 src/conv-ccs/ccs-client.C create mode 100644 src/conv-ccs/ccs-client.h create mode 100644 src/conv-ccs/ccs-server.C create mode 100644 src/conv-ccs/ccs-server.h create mode 100644 src/conv-ccs/conv-ccs.C create mode 100644 src/conv-ccs/conv-ccs.h create mode 100644 src/conv-ccs/middle-ccs.C create mode 100644 src/pup/pup.h create mode 100644 src/pup/pup_c.C create mode 100644 src/pup/pup_c.h create mode 100644 src/pup/pup_c_functions.h create mode 100644 src/pup/pup_cmialloc.C create mode 100644 src/pup/pup_cmialloc.h create mode 100755 src/pup/pup_f.f90.sh create mode 100644 src/pup/pup_mpi.h create mode 100644 src/pup/pup_paged.C create mode 100644 src/pup/pup_paged.h create mode 100644 src/pup/pup_stl.h create mode 100644 src/pup/pup_toNetwork.C create mode 100644 src/pup/pup_toNetwork.h create mode 100644 src/pup/pup_toNetwork4.C create mode 100644 src/pup/pup_toNetwork4.h create mode 100644 src/pup/pup_util.C create mode 100644 src/pup/pup_xlater.C create mode 100644 src/pup/pupf.h create mode 100644 tests/pup_test/CMakeLists.txt create mode 100644 tests/pup_test/pup_test.cpp diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 05a8189..aa05f23 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1 +1,15 @@ target_include_directories(reconverse PUBLIC .) + +# Install PUP headers +install(FILES + ../src/pup/pup.h + ../src/pup/pup_c.h + ../src/pup/pup_stl.h + ../src/pup/pup_toNetwork.h + ../src/pup/pup_toNetwork4.h + ../src/pup/pup_paged.h + ../src/pup/pup_mpi.h + ../src/pup/pup_cmialloc.h + ../src/pup/pupf.h + ../src/pup/pup_c_functions.h + DESTINATION include/pup) diff --git a/include/charm-api.h b/include/charm-api.h new file mode 100644 index 0000000..ed6815d --- /dev/null +++ b/include/charm-api.h @@ -0,0 +1,9 @@ +#ifndef CHARM_API_H +#define CHARM_API_H + +// Stub file for compatibility - Charm++ API functions +// This is a simplified version for reconverse + +#include "converse.h" + +#endif // CHARM_API_H \ No newline at end of file diff --git a/include/conv-rdma.h b/include/conv-rdma.h index a53bfa9..de4e9c2 100644 --- a/include/conv-rdma.h +++ b/include/conv-rdma.h @@ -2,7 +2,9 @@ #define _CONV_RDMA_H #include "cmirdmautils.h" +#include "pup.h" #include +#include // User specified configuration // TODO: move to a better location @@ -184,6 +186,34 @@ class CmiNcpyBuffer { deregMode, ref, refAckInfo); } + void pup(PUP::er &p) { + // Serialize pointer as uintptr_t for portability + uintptr_t ptr_val = (uintptr_t)ptr; + p(ptr_val); + p(cnt); + p(pe); + p(regMode); + p(deregMode); + + // Serialize ref pointer as uintptr_t for portability + uintptr_t ref_val = (uintptr_t)ref; + p(ref_val); + + // Serialize refAckInfo pointer as uintptr_t for portability + uintptr_t refAckInfo_val = (uintptr_t)refAckInfo; + p(refAckInfo_val); + + p(isRegistered); + // Note: layerInfo is not serialized as it's machine-specific + + // On unpacking, restore the pointers + if (p.isUnpacking()) { + ptr = (const void *)ptr_val; + ref = (const void *)ref_val; + refAckInfo = (const void *)refAckInfo_val; + } + } + void init(const void *ptr_, size_t cnt_, unsigned short int regMode_ = CMK_BUFFER_REG, unsigned short int deregMode_ = CMK_BUFFER_DEREG) { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 811834a..17c3c9e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,13 +2,27 @@ target_include_directories(reconverse PRIVATE .) target_sources(reconverse PRIVATE conv-conds.cpp convcore.cpp random.cpp scheduler.cpp cpuaffinity.cpp collectives.cpp comm_backend/comm_backend_internal.cpp threads.cpp cldb.rand.cpp cldb.cpp cmirdmautils.cpp - conv-rdma.cpp conv-topology.cpp msgmgr.cpp) + conv-rdma.cpp conv-topology.cpp msgmgr.cpp + pup/pup_xlater.C pup/pup_toNetwork.C pup/pup_c.C + pup/pup_paged.C pup/pup_toNetwork4.C pup/pup_util.C + pup/pup_cmialloc.C) target_include_directories( reconverse PRIVATE $ $) +# Add pup directory to include path +target_include_directories(reconverse PUBLIC + $ + $) add_subdirectory(comm_backend) +# Set pointer size flag for PUP +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(CMK_SIZET_64BIT ON) +else() + set(CMK_SIZET_64BIT OFF) +endif() + # configure needs to be called after add_subdirectory(comm_backend) configure_file(converse_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/converse_config.h) diff --git a/src/conv-ccs/ccs-auth.C b/src/conv-ccs/ccs-auth.C new file mode 100644 index 0000000..0086528 --- /dev/null +++ b/src/conv-ccs/ccs-auth.C @@ -0,0 +1,289 @@ +/******************************************************* +Hashing and authentication routines for CCS. + +Orion Sky Lawlor, olawlor@acm.org, 7/21/2001 +*/ + +#include +#include +// #include "sockRoutines.h" +#include "conv-ccs.h" +#include "ccs-auth.h" + +/* Parse this secret key as a hex string*/ +int CCS_AUTH_makeSecretKey(const char *str,CcsSec_secretKey *key) +{ + int i; + memset(key->data,0,sizeof(CcsSec_secretKey)); + for (i=0;idata);i++) { + int cur=0; + char tmp[3]; + tmp[0]=str[2*i+0]; + tmp[1]=str[2*i+1]; + if (tmp[1]==0 || tmp[1]==' ' || tmp[1]=='\n') tmp[1]='0'; /*zero-pad*/ + tmp[2]=0; + if (1!=sscanf(tmp,"%d",&cur)) break; + key->data[i]=(unsigned char)cur; + } + if (i==0) return 0; + else return 1; +} + + +/************************************************************* +Perform one round of the SHA-1 message hash. Input is a set of +16 32-bit native words (512 bits); output is 5 32-bit native +words (160 bits). Because it uses native arithmetic, the +implementation works equally well with 32 and 64-bit big- +and little-endian systems. However, when the input or output +is interpreted as bytes, they should be considered big-endian. +The speed is about 400,000 transformed blocks per second on a +1 GHz machine. + +Implemented and placed in the public domain by Steve Reid +Collected by Wei Dai (http://www.eskimo.com/~weidai/cryptlib.html) + +Adapted for Charm++ by Orion Sky Lawlor, olawlor@acm.org, 7/20/2001 +*/ +/*Contains at least the low 32 bits of a big-endian integer.*/ +typedef unsigned int word32; +typedef unsigned char byte8; + +static void SHA1_init(word32 *state) +{ + state[0] = 0x67452301u; + state[1] = 0xEFCDAB89u; + state[2] = 0x98BADCFEu; + state[3] = 0x10325476u; + state[4] = 0xC3D2E1F0u; +} + +static word32 rotlFixed(word32 x, word32 y) +{ +#if defined(_MSC_VER) || defined(__BCPLUSPLUS__) + return y ? _lrotl(x, y) : x; +#elif defined(__MWERKS__) && TARGET_CPU_PPC + return y ? __rlwinm(x,y,0,31) : x; +#else /*Default C version*/ + return ((0xFFffFFffu)&(x<>(32-y)); +#endif +} + +#define blk0(i) (W[i] = data[i]) +#define blk1(i) (W[i&15] = rotlFixed(W[(i+13)&15]^W[(i+8)&15]^W[(i+2)&15]^W[i&15],1)) + +#define f1(x,y,z) (z^(x&(y^z))) +#define f2(x,y,z) (x^y^z) +#define f3(x,y,z) ((x&y)|(z&(x|y))) +#define f4(x,y,z) (x^y^z) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=f1(w,x,y)+blk0(i)+0x5A827999u+rotlFixed(v,5);w=rotlFixed(w,30); +#define R1(v,w,x,y,z,i) z+=f1(w,x,y)+blk1(i)+0x5A827999u+rotlFixed(v,5);w=rotlFixed(w,30); +#define R2(v,w,x,y,z,i) z+=f2(w,x,y)+blk1(i)+0x6ED9EBA1u+rotlFixed(v,5);w=rotlFixed(w,30); +#define R3(v,w,x,y,z,i) z+=f3(w,x,y)+blk1(i)+0x8F1BBCDCu+rotlFixed(v,5);w=rotlFixed(w,30); +#define R4(v,w,x,y,z,i) z+=f4(w,x,y)+blk1(i)+0xCA62C1D6u+rotlFixed(v,5);w=rotlFixed(w,30); + +static void SHA1_transform(word32 *state, const word32 *data) +{ + word32 W[16]; + /* Copy context->state[] to working vars */ + word32 a = state[0]; + word32 b = state[1]; + word32 c = state[2]; + word32 d = state[3]; + word32 e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; +} + +/******************************************************** +Compute the signed SHA-1 hash of this 52-byte input. +The 52 bytes comes about because a SHA block is 64 bytes, +minus an 8-byte length block and 4-byte end-of-message +code. It is permissible for in to equal out. + +The resulting hash code is 20 bytes long. +*/ +static void SHA1_hash(const byte8 *in,SHA1_hash_t *out) +{ + int i; +#define SHA1_data_len 16 /*Length of input data (words)*/ +#define SHA1_hash_len 5 /*Length of output hash code (words)*/ + word32 message[SHA1_data_len]; + word32 hash[SHA1_hash_len]; + + /*Assemble the message from the user data by + interpreting the bytes as big-endian words.*/ + for (i=0;idata[i*4+0]=0xffu & (hash[i]>>24); + out->data[i*4+1]=0xffu & (hash[i]>>16); + out->data[i*4+2]=0xffu & (hash[i]>> 8); + out->data[i*4+3]=0xffu & (hash[i]>> 0); + } +} + +#if SHA1_TEST_DRIVER +/* Tiny test driver routine-- should print out: +F9693B3AE7791C4ACE70CE31E4C2213F21CE900A +*/ +int main(int argc,char *argv[]) +{ + int i; + SHA1_hash_t h; + byte8 message[52]; memset(message,0,52); + message[0]=0x01; + SHA1_hash(message, &h); + for (i=0;idata,b->data,sizeof(SHA1_hash_t)); +} + +/******************************************************** +Authentication routines: create a hash code; compare +hash codes. +*/ + +/*Create a hash code of this secret key, varying "salt" value, +and (optional, may be NULL) request header. +*/ +void CCS_AUTH_hash(const CcsSec_secretKey *key,unsigned int salt, + const CcsMessageHeader *hdrOrNull,SHA1_hash_t *out) +{ + /*Fill the message buffer*/ + byte8 mess[64]; + byte8 *messCur=mess; + + memset(mess,0,64); + memcpy(messCur,key,sizeof(CcsSec_secretKey)); + messCur+=sizeof(CcsSec_secretKey); + + *(ChMessageInt_t *)messCur=ChMessageInt_new(salt); + messCur+=sizeof(ChMessageInt_t); + + if (hdrOrNull!=NULL) { + const int headerBytes=16; /*Only copy start of header.*/ + memcpy(messCur,hdrOrNull,headerBytes); + } + + SHA1_hash(mess,out); +} + +/*Create a hash code as above, and compare it to the given code.*/ +int CCS_AUTH_differ(const CcsSec_secretKey *key,unsigned int salt, + const CcsMessageHeader *hdrOrNull,SHA1_hash_t *given) +{ + SHA1_hash_t cur; + CCS_AUTH_hash(key,salt,hdrOrNull,&cur); + return SHA1_differ(&cur,given); +} + +/******************************************************** +Randomness routines: return a good 32-bit random number. +*/ +#include + +#if defined(_WIN32) +#include +#else /*UNIX machine*/ +#include +#include +#endif + +void CCS_RAND_new(CCS_RAND_state *s) +{ + int i,randFD; + static int newCount=0; + byte8 tmp[sizeof(s->state)]; + + /* State buffer starts out uninitialized. */ + /* XOR in a linear counter */ + s->state[32] ^= newCount++; + +/*Fill the state buffer with random noise*/ + +#if defined(_WIN32) + _ftime((struct _timeb *)tmp); + for (i=0;istate);i++) + s->state[i]^=tmp[i]; +#else /*UNIX machine*/ + /* XOR the current time of day into the state buffer*/ + gettimeofday((struct timeval *)tmp,NULL); + for (i=0;istate);i++) + s->state[i]^=tmp[i]; + + /* XOR bytes from /dev/urandom into the state buffer*/ + randFD=open("/dev/urandom",O_RDONLY); + if (randFD!=-1) { + if (sizeof(s->state)==read(randFD,tmp,sizeof(s->state))) + for (i=0;istate);i++) + s->state[i]^=tmp[i]; + close(randFD); + } +#endif +} + +word32 CCS_RAND_next(CCS_RAND_state *s) { + SHA1_hash_t ret; + /*Stir the state*/ + (*(int *)(s->state))++; + SHA1_hash(s->state,&ret); + return *(word32 *)(&ret); +} + + + + + + + + + + + + diff --git a/src/conv-ccs/ccs-auth.h b/src/conv-ccs/ccs-auth.h new file mode 100644 index 0000000..026fc10 --- /dev/null +++ b/src/conv-ccs/ccs-auth.h @@ -0,0 +1,47 @@ +/************************************************** + CCS Authentication utility routines + +Orion Sky Lawlor, olawlor@acm.org, 7/23/2001 +*/ +#ifndef __CCS_AUTH_H +#define __CCS_AUTH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*A secret key, used to authenticate a client or server. +This could be human-readable text, a random one-time pad, +some shared common knowledge, or any combination. +*/ +typedef struct { + unsigned char data[16]; +} CcsSec_secretKey; +int CCS_AUTH_makeSecretKey(const char *str,CcsSec_secretKey *key); + + +/*The output of a SHA-1 hash algorithm*/ +typedef struct { + unsigned char data[20]; +} SHA1_hash_t; + +void CCS_AUTH_hash(const CcsSec_secretKey *key,unsigned int salt, + const CcsMessageHeader *hdrOrNull,SHA1_hash_t *out); +int CCS_AUTH_differ(const CcsSec_secretKey *key,unsigned int salt, + const CcsMessageHeader *hdrOrNull,SHA1_hash_t *given); + + +/*Strong (but rather slow) random stream*/ +typedef struct { + unsigned char state[64]; /*Random number stream state*/ +} CCS_RAND_state; + +void CCS_RAND_new(CCS_RAND_state *s); +unsigned int CCS_RAND_next(CCS_RAND_state *s); + +#ifdef __cplusplus +} +#endif + +#endif /* def(thisHeader) */ + diff --git a/src/conv-ccs/ccs-builtins.C b/src/conv-ccs/ccs-builtins.C new file mode 100644 index 0000000..a2059a5 --- /dev/null +++ b/src/conv-ccs/ccs-builtins.C @@ -0,0 +1,746 @@ +/***************************************************************************** + * A few useful built-in CCS handlers. + *****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "converse.h" +#include "ckhashtable.h" +#include "pup.h" +#include "pup_toNetwork.h" +#include "debug-conv++.h" +#include "conv-ccs.h" +#include "sockRoutines.h" +#include "queueing.h" +#include "ccs-builtins.h" + +#ifdef __MINGW_H +#include "process.h" +#endif + +#if CMK_CCS_AVAILABLE + +void ccs_getinfo(char *msg); + +/********************************************** + "ccs_killport"-- takes one 4-byte big-endian port number + Register a "client kill port". When this program exits, + it will connect to this TCP port and write "die\n\0" it. +*/ + +typedef struct killPortStruct{ + skt_ip_t ip; + unsigned int port; + struct killPortStruct *next; +} killPortStruct; +/*Only 1 kill list per node-- no Cpv needed*/ +static killPortStruct *killList=NULL; + +static void ccs_killport(char *msg) +{ + killPortStruct *oldList=killList; + int port=ChMessageInt(*(ChMessageInt_t *)(msg+CmiReservedHeaderSize)); + skt_ip_t ip; + unsigned int connPort; + CcsCallerId(&ip,&connPort); + killList=(killPortStruct *)malloc(sizeof(killPortStruct)); + killList->ip=ip; + killList->port=port; + killList->next=oldList; + CmiFree(msg); +} +/*Send any registered clients kill messages before we exit*/ +static int noMoreErrors(SOCKET skt, int c, const char *m) {return -1;} +void CcsImpl_kill(void) +{ + skt_set_abort(noMoreErrors); + while (killList!=NULL) + { + SOCKET fd=skt_connect(killList->ip,killList->port,20); + if (fd!=INVALID_SOCKET) { + skt_sendN(fd,"die\n",strlen("die\n")+1); + skt_close(fd); + } + killList=killList->next; + } +} + +/********************************************** + "ccs_killpe"-- kills the executing processor + Used for fault-tolerance testing: terminate the processor. +*/ + +#include + +static void ccs_killpe(char *msg) { +#if CMK_HAS_KILL + kill(getpid(), 9); +#else + CmiAbort("ccs_killpe() not supported!"); +#endif +} + +/************************************************* +List interface: + This lets different parts of a Charm++ program register +"list retrieval functions", which expose some aspect of a +running program. Example lists include: all readonly globals, +messages waiting in a queue, or all extant array elements. + + "ccs_list_len" (4-byte character count, null-terminated ASCII string) +Return the number of items currently in the list at the given path. + + "ccs_list_items.bin" (4-byte start, 4-byte end+1,4-byte extra +data count n, n-byte extra data, 4-byte character count c, c-byte +ASCII request string [without NULL terminator]) +Return the given items (from start to end) of the given queue, +formatted as raw network binary data. + + "ccs_list_items.fmt" [parameters as for .bin] +Return the given items, formatted as tagged network binary data. + + "ccs_list_items.txt" [parameters as for .bin] +Return the given items, formatted as ASCII text. +*/ + +static void CpdListBoundsCheck(CpdListAccessor *l,int &lo,int &hi) +{ + if (l->checkBoundary()) { + int len=l->getLength(); + if (lo<0) lo=0; + if (hi>len) hi=len; + } +} + +typedef CkHashtableTslow CpdListTable_t; +CpvStaticDeclare(CpdListTable_t *,cpdListTable); + +/** + Return the list at this (null-terminated ASCII) path. +*/ +static CpdListAccessor *CpdListLookup(const char *path) +{ + CpdListAccessor *acc=CpvAccess(cpdListTable)->get(path); + if (acc==NULL) { + CmiError("CpdListAccessor> Unrecognized list path '%s'\n",path); + return NULL; + } + return acc; +} + +/** + Return a CpdListAccessor, given a network string containing the + list path. A network string is a big-endian 32-bit "length" + field, followed by a null-terminated ASCII string of that length. +*/ +static CpdListAccessor *CpdListLookup(const ChMessageInt_t *lenAndPath) +{ + static const int CpdListMaxLen=80; + int len=ChMessageInt(lenAndPath[0]); + const char *path=(const char *)(lenAndPath+1); + char pathBuf[CpdListMaxLen+1]; //Temporary null-termination buffer + if ((len<0) || (len>CpdListMaxLen)) { + CmiError("CpdListAccessor> Invalid list path length %d!\n",len); + return NULL; //Character count is invalid + } + strncpy(pathBuf,path,len); + pathBuf[len]=0; //Ensure string is null-terminated + return CpdListLookup(pathBuf); +} + +//CCS External access routines: + +//Get the length of the given list: +static void CpdList_ccs_list_len(char *msg) +{ + const ChMessageInt_t *req=(const ChMessageInt_t *)(msg+CmiReservedHeaderSize); + CpdListAccessor *acc=CpdListLookup(req); + if (acc!=NULL) { + ChMessageInt_t reply=ChMessageInt_new(acc->getLength()); + CcsSendReply(sizeof(reply),(void *)&reply); + } + CmiFree(msg); +} + +//Read a list contents request header: +// first item to send, 4-byte network integer +// last item+1 to send, 4-byte network integer +// extra data length, 4-byte network integer +// extra data, list-defined bytes +// list path length, 4-byte network integer (character count) +// list path name, null-terminated ASCII +static CpdListAccessor *CpdListHeader_ccs_list_items(char *msg, + CpdListItemsRequest &h) +{ + int msgLen=CmiSize((void *)msg)-CmiReservedHeaderSize; + CpdListAccessor *ret=NULL; + const ChMessageInt_t *req=(const ChMessageInt_t *)(msg+CmiReservedHeaderSize); + h.lo=ChMessageInt(req[0]); // first item to send + h.hi=ChMessageInt(req[1]); // last item to send+1 + h.extraLen=ChMessageInt(req[2]); // extra data length + if (h.extraLen>=0 + && ((int)(3*sizeof(ChMessageInt_t)+h.extraLen))pup(p,req); + p.syncComment(PUP::sync_end_array); +} + +static void CpdList_ccs_list_items_txt(char *msg) +{ + CpdListItemsRequest req; + CpdListAccessor *acc=CpdListHeader_ccs_list_items(msg,req); + if(acc == NULL) CmiPrintf("ccs-builtins> Null Accessor--bad list name (txt)\n"); + if (acc!=NULL) { + int bufLen; + { + PUP::sizerText p; pupCpd(p,acc,req); bufLen=p.size(); + } + char *buf=new char[bufLen]; + { + PUP::toText p(buf, bufLen); pupCpd(p,acc,req); + if (p.size()!=bufLen) + CmiError("ERROR! Sizing/packing length mismatch for %s list pup function!\n", + acc->getPath()); + } + CcsSendReply(bufLen,(void *)buf); + delete[] buf; + } + CmiFree(msg); +} + +static void CpdList_ccs_list_items_set(char *msg) +{ + CpdListItemsRequest req; + CpdListAccessor *acc=CpdListHeader_ccs_list_items(msg,req); + if(acc == NULL) CmiPrintf("ccs-builtins> Null Accessor--bad list name (set)\n"); + else { + PUP_toNetwork_unpack p(req.extra); + pupCpd(p,acc,req); + if (p.size()!=req.extraLen) + CmiPrintf("Size mismatch during ccs_list_items.set: client sent %d bytes, but %zu bytes used!\n", + req.extraLen,p.size()); + } + CmiFree(msg); +} + +/** gather information about the machine we're currently running on */ +void CpdMachineArchitecture(char *msg) { + char reply[8]; // where we store our reply + reply[0]=CHARMDEBUG_MAJOR; + reply[1]=CHARMDEBUG_MINOR; + // decide if we are 32 bit (1) or 64 bit (2) + reply[2] = 0; + if (sizeof(char*) == 4) reply[2] = 1; + else if (sizeof(char*) == 8) reply[2] = 2; + // decide if we are little endian (1) or big endian (2) + reply[3] = 0; + int value = 1; + char firstByte = *((char*)&value); + if (firstByte == 1) reply[3] = 1; + else reply[3] = 2; + // get the size of an "int" + reply[4] = sizeof(int); + // get the size of an "long" + reply[5] = sizeof(long); +#if CMK_LONG_LONG_DEFINED + // get the size of an "long long" + reply[6] = sizeof(long long); +#else + // Per Filippo, the debugger will be fine with this. It should never + // come up, since configure didn't detect support for `long long` on + // the machine. + reply[6] = 0; +#endif + // get the size of an "bool" + reply[7] = sizeof(bool); + CcsSendReply(8, (void*)reply); + CmiFree(msg); +} + +static void CpdList_ccs_list_items_fmt(char *msg) +{ + CpdListItemsRequest req; + CpdListAccessor *acc=CpdListHeader_ccs_list_items(msg,req); + if (acc!=NULL) { + int bufLen; + { + PUP_toNetwork_sizer ps; + PUP_fmt p(ps); + pupCpd(p,acc,req); + bufLen=ps.size(); + } + char *buf=new char[bufLen]; + { + PUP_toNetwork_pack pp(buf); + PUP_fmt p(pp); + pupCpd(p,acc,req); + if (pp.size()!=bufLen) + CmiError("ERROR! Sizing/packing length mismatch for %s list pup function (%d sizing, %zu packing)\n", + acc->getPath(),bufLen,pp.size()); + } + CcsSendReply(bufLen,(void *)buf); + delete[] buf; + } + CmiFree(msg); +} + + + + +//Introspection object-- extract a list of CpdLists! +class CpdList_introspect : public CpdListAccessor { + CpdListTable_t *tab; +public: + CpdList_introspect(CpdListTable_t *tab_) :tab(tab_) { } + virtual const char *getPath(void) const { return "converse/lists";} + virtual size_t getLength(void) const { + size_t len=0; + CkHashtableIterator *it=tab->iterator(); + while (NULL!=it->next()) len++; + delete it; + return len; + } + virtual void pup(PUP::er &p,CpdListItemsRequest &req) { + CkHashtableIterator *it=tab->iterator(); + void *objp; + int curObj=0; + while (NULL!=(objp=it->next())) { + if (curObj>=req.lo && curObjgetPath(); + beginItem(p,curObj); + p.comment("name"); + p(pathName,strlen(pathName)); + } + curObj++; + } + } +}; + + + + +#endif /*CMK_CCS_AVAILABLE*/ +/*We have to include these virtual functions, even when CCS is +disabled, to avoid bizarre link-time errors.*/ + +// C++ and C client API +void CpdListRegister(CpdListAccessor *acc) +#if CMK_CCS_AVAILABLE +{ + CpvAccess(cpdListTable)->put(acc->getPath())=acc; +} +#else +{ } +#endif + +void CpdListRegister_c(const char *path, + CpdListLengthFn_c len,void *lenParam, + CpdListItemsFn_c items,void *itemsParam,int checkBoundary) +#if CMK_CCS_AVAILABLE +{ + CpdListRegister(new CpdListAccessor_c(path, + len,lenParam,items,itemsParam,checkBoundary!=0?true:false)); +} +#else +{ } +#endif + +#if CMK_CCS_AVAILABLE + + +// Initialization + +static void CpdListInit(void) { + CpvInitialize(CpdListTable_t *,cpdListTable); + CpvAccess(cpdListTable)=new CpdListTable_t(31,0.5, + CkHashFunction_string,CkHashCompare_string); + CpdListRegister(new CpdList_introspect(CpvAccess(cpdListTable))); + + CcsRegisterHandler("ccs_list_len",(CmiHandler)CpdList_ccs_list_len); + CcsRegisterHandler("ccs_list_items.txt",(CmiHandler)CpdList_ccs_list_items_txt); + CcsRegisterHandler("ccs_list_items.fmt",(CmiHandler)CpdList_ccs_list_items_fmt); + CcsRegisterHandler("ccs_list_items.set",(CmiHandler)CpdList_ccs_list_items_set); + CcsRegisterHandler("debug/converse/arch",(CmiHandler)CpdMachineArchitecture); +} + +#if CMK_WEB_MODE +/****************************************************** +Web performance monitoring interface: + Clients will register for performance data with +processor 0 by calling the "perf_monitor" CCS request. +Every WEB_INTERVAL (few seconds), this code +calls all registered web performance functions on all processors. +The resulting integers are sent back to the client as a +CCS reply. + +The current reply format is ASCII and rather nasty: +it's the string "perf" followed by space-separated list +of the performance functions for each processor. By default, +only two performance functions are registered: the current +processor utilization, in percent; and the current queue length. + +The actual call sequence is: +CCS Client->CWebHandler->... (processor 0) + ...->CWeb_Collect->... (all processors) +...->CWeb_Reduce->CWeb_Deliver (processor 0 again) +*/ + +#if 0 +# define WEBDEBUG(x) CmiPrintf x +#else +# define WEBDEBUG(x) /*empty*/ +#endif + +#define WEB_INTERVAL 1000 /*Time, in milliseconds, between performance snapshots*/ +#define MAXFNS 20 /*Largest number of performance functions to expect*/ + +typedef struct { + char hdr[CmiReservedHeaderSize]; + int fromPE;/*Source processor*/ + int perfData[MAXFNS];/*Performance numbers*/ +} CWeb_CollectedData; + +/*This needs to be made into a list of registered clients*/ +static int hasApplet=0; +static CcsDelayedReply appletReply; + +typedef int (*CWebFunction)(void); +static CWebFunction CWebPerformanceFunctionArray[MAXFNS]; +static int CWebNoOfFns; +static int CWeb_ReduceIndex; +static int CWeb_CollectIndex; + +/*Deliver the reduced web performance data to the waiting client: +*/ +static int collectedCount; +static CWeb_CollectedData **collectedValues; + +static void CWeb_Deliver(void) +{ + int i,j; + + if (hasApplet) { + WEBDEBUG(("CWeb_Deliver to applet\n")); + /*Send the performance data off to the applet*/ + int reply_len = 6+14+CmiNumPes()*CWebNoOfFns; + char *reply=(char *)malloc(reply_len); + snprintf(reply,reply_len,"perf"); + + for(i=0; iperfData[j]); + strcat(reply,buf); + } + } + CcsSendDelayedReply(appletReply,strlen(reply) + 1, reply); + free(reply); + hasApplet=0; + } + else + WEBDEBUG(("CWeb_Deliver (NO APPLET)\n")); + + /* Free saved performance data */ + for(i = 0; i < CmiNumPes(); i++){ + CmiFree(collectedValues[i]); + collectedValues[i] = 0; + } + collectedCount = 0; +} + +/*On PE 0, this handler accumulates all the performace data +*/ +static void CWeb_Reduce(void *msg){ + CWeb_CollectedData *cur,*prev; + int src; + if(CmiMyPe() != 0){ + CmiAbort("CWeb performance data sent to wrong processor...\n"); + } + WEBDEBUG(("CWeb_Reduce")); + cur=(CWeb_CollectedData *)msg; + src=cur->fromPE; + prev = collectedValues[src]; /* Previous value, ideally 0 */ + collectedValues[src] = cur; + if(prev == 0) collectedCount++; + else CmiFree(prev); /*<- caused by out-of-order perf. data delivery*/ + + if(collectedCount == CmiNumPes()){ + CWeb_Deliver(); + } +} + +/*On each PE, this handler collects the performance data +and sends it to PE 0. +*/ +static void CWeb_Collect(void) +{ + CWeb_CollectedData *msg; + int i; + + WEBDEBUG(("CWeb_Collect on %d\n",CmiMyPe())); + msg = (CWeb_CollectedData *)CmiAlloc(sizeof(CWeb_CollectedData)); + msg->fromPE = CmiMyPe(); + + /* Evaluate each performance function*/ + for(i = 0; i < CWebNoOfFns; i++) + msg->perfData[i] = CWebPerformanceFunctionArray[i] (); + + /* Send result off to node 0 */ + CmiSetHandler(msg, CWeb_ReduceIndex); + CmiSyncSendAndFree(0, sizeof(CWeb_CollectedData), msg); + + /* Re-call this function after a delay */ + CcdCallFnAfter((CcdVoidFn)CWeb_Collect, 0, WEB_INTERVAL); +} + +void CWebPerformanceRegisterFunction(CWebFunction fn) +{ + if (CmiMyRank()!=0) return; /* Should only register from rank 0 */ + if (CWebNoOfFns>=MAXFNS) CmiAbort("Registered too many CWebPerformance functions!"); + CWebPerformanceFunctionArray[CWebNoOfFns] = fn; + CWebNoOfFns++; +} + +/*This is called on PE 0 by clients that wish +to receive performance data. +*/ +static void CWebHandler(void){ + if(CcsIsRemoteRequest()) { + static int startedCollection=0; + + WEBDEBUG(("CWebHandler request on %d\n",CmiMyPe())); + hasApplet=1; + appletReply=CcsDelayReply(); + + if(startedCollection == 0){ + WEBDEBUG(("Starting data collection on every processor\n")); + int i; + startedCollection=1; + collectedCount=0; + collectedValues = (CWeb_CollectedData **)malloc(sizeof(void *) * CmiNumPes()); + for(i = 0; i < CmiNumPes(); i++) + collectedValues[i] = 0; + + /*Start collecting data on each processor*/ + for(i = 0; i < CmiNumPes(); i++){ + char *msg = (char *)CmiAlloc(CmiReservedHeaderSize); + CmiSetHandler(msg, CWeb_CollectIndex); + CmiSyncSendAndFree(i, CmiReservedHeaderSize,msg); + } + } + } +} + +/** This "usage" section keeps track of percent of wall clock time +spent actually processing messages on each processor. +It's a simple performance measure collected by the CWeb framework. +**/ +struct CWebModeStats { +public: + double beginTime; ///< Start of last collection interval + double startTime; ///< Start of last busy time + double usedTime; ///< Total busy time in last collection interval + int PROCESSING; ///< If 1, processor is busy +}; +CpvStaticDeclare(CWebModeStats *,cwebStats); + +/* Called to begin a collection interval +*/ +static void usageReset(CWebModeStats *stats, double curWallTime) +{ + stats->beginTime=curWallTime; + stats->usedTime = 0.; +} + +/* Called when processor becomes busy +*/ +static void usageStart(CWebModeStats *stats) +{ + stats->startTime = CmiWallTimer(); + stats->PROCESSING = 1; +} + +/* Called when processor becomes idle +*/ +static void usageStop(CWebModeStats *stats) +{ + stats->usedTime += CmiWallTimer() - stats->startTime; + stats->PROCESSING = 0; +} + +/* Call this when the program is started + -> Whenever traceModuleInit would be called + -> -> see conv-core/convcore.C +*/ +static void initUsage() +{ + CpvInitialize(CWebModeStats *, cwebStats); + CWebModeStats *stats=new CWebModeStats; + CpvAccess(cwebStats)=stats; + usageReset(stats, CmiWallTimer()); + usageStart(stats); + CcdCallOnConditionKeep(CcdPROCESSOR_BEGIN_BUSY,(CcdCondFn)usageStart,stats); + CcdCallOnConditionKeep(CcdPROCESSOR_BEGIN_IDLE,(CcdCondFn)usageStop,stats); +} + +static int getUsage(void) +{ + int usage = 0; + double time = CmiWallTimer(); + CWebModeStats *stats=CpvAccess(cwebStats); + double totalTime = time - stats->beginTime; + + if(stats->PROCESSING) + { /* Lock in current CPU usage */ + usageStop(stats); usageStart(stats); + } + if(totalTime > 0.) + usage = (int)(0.5 + 100 *stats->usedTime/totalTime); + usageReset(stats,time); + + return usage; +} + +static int getSchedQlen(void) +{ + return(CqsLength((Queue)CpvAccess(CsdSchedQueue))); +} + +#endif /*CMK_WEB_MODE*/ + +#if ! CMK_WEB_MODE +static void CWeb_Invalid(void) +{ + CmiAbort("Invalid web mode handler invoked!\n"); +} +#endif + +void CWebInit(void) +{ +#if CMK_WEB_MODE + CcsRegisterHandler("perf_monitor", (CmiHandler)CWebHandler); + + CmiAssignOnce(&CWeb_CollectIndex, CmiRegisterHandler((CmiHandler)CWeb_Collect)); + CmiAssignOnce(&CWeb_ReduceIndex, CmiRegisterHandler((CmiHandler)CWeb_Reduce)); + + initUsage(); + CWebPerformanceRegisterFunction(getUsage); + CWebPerformanceRegisterFunction(getSchedQlen); +#else + /* always maintain the consistent CmiHandler table */ + /* which is good for heterogeneous clusters */ + CmiRegisterHandler((CmiHandler)CWeb_Invalid); + CmiRegisterHandler((CmiHandler)CWeb_Invalid); +#endif +} + + +void CcsBuiltinsInit(char **argv) +{ + CcsRegisterHandler("ccs_getinfo",(CmiHandler)ccs_getinfo); + CcsRegisterHandler("ccs_killport",(CmiHandler)ccs_killport); + CcsRegisterHandler("ccs_killpe",(CmiHandler)ccs_killpe); + CWebInit(); + CpdListInit(); +} + + +#endif /*CMK_CCS_AVAILABLE*/ + +void PUP_fmt::fieldHeader(typeCode_t typeCode,int nItems) { + // Compute and write intro byte: + lengthLen_t ll; + if (nItems==1) ll=lengthLen_single; + else if (nItems<256) ll=lengthLen_byte; + else ll=lengthLen_int; + // CmiPrintf("Intro byte: l=%d t=%d\n",(int)ll,(int)typeCode); + byte intro=(((int)ll)<<4)+(int)typeCode; + p(intro); + // Compute and write length: + switch(ll) { + case lengthLen_single: break; // Single item + case lengthLen_byte: { + byte l=nItems; + p(l); + } break; + case lengthLen_int: { + p(nItems); + } break; + case lengthLen_long: CmiAbort("Should not have reached here!"); break; + }; +} + +void PUP_fmt::comment(const char *message) { + size_t nItems=strlen(message); + fieldHeader(typeCode_comment,nItems); + p((char *)message,nItems); +} +void PUP_fmt::synchronize(unsigned int m) { + fieldHeader(typeCode_sync,1); + p(m); +} + +void PUP_fmt::pup_buffer(void *&ptr,size_t n,size_t itemSize,PUP::dataType t) { + bytes(ptr, n, itemSize, t); +} + +void PUP_fmt::pup_buffer(void *&ptr,size_t n, size_t itemSize, PUP::dataType t, std::function allocate, std::function deallocate){ + bytes(ptr, n, itemSize, t); +} + +void PUP_fmt::bytes(void *ptr,size_t n,size_t itemSize,PUP::dataType t) { + if(itemSize > INT_MAX || n > INT_MAX || itemSize*n > INT_MAX) + CmiAbort("Ccs does not support messages greater than INT_MAX...\n"); + switch(t) { + case PUP::Tchar: + case PUP::Tuchar: + case PUP::Tbyte: + fieldHeader(typeCode_byte,n); + p.bytes(ptr,n,itemSize,t); + break; + case PUP::Tshort: case PUP::Tint: + case PUP::Tushort: case PUP::Tuint: + case PUP::Tbool: + fieldHeader(typeCode_int,n); + p.bytes(ptr,n,itemSize,t); + break; + // treat "long" and "pointer" as 8-bytes, in conformity with pup_toNetwork.C + case PUP::Tlong: case PUP::Tlonglong: + case PUP::Tulong: case PUP::Tulonglong: + fieldHeader(typeCode_long,n); + p.bytes(ptr,n,itemSize,t); + break; + case PUP::Tfloat: + fieldHeader(typeCode_float,n); + p.bytes(ptr,n,itemSize,t); + break; + case PUP::Tdouble: case PUP::Tlongdouble: + fieldHeader(typeCode_double,n); + p.bytes(ptr,n,itemSize,t); + break; + case PUP::Tpointer: + fieldHeader(typeCode_pointer,n); + p.bytes(ptr,n,itemSize,t); + break; + default: CmiAbort("Unrecognized type code in PUP_fmt::bytes"); + }; +} + + diff --git a/src/conv-ccs/ccs-builtins.h b/src/conv-ccs/ccs-builtins.h new file mode 100644 index 0000000..f768dda --- /dev/null +++ b/src/conv-ccs/ccs-builtins.h @@ -0,0 +1,41 @@ +/** + A PUP_fmt inserts a 1-byte data format code + before each pup'd data item. This makes it possible + to unpack items without using PUP. +*/ +class PUP_fmt : public PUP::wrap_er { + typedef PUP::er parent; + + typedef unsigned char byte; + /** + The introductory byte for each field is bit-coded as: + | unused(2) | lengthLen(2) | typeCode(4) | + */ + typedef enum { + lengthLen_single=0, // field is a single item + lengthLen_byte=1, // following 8 bits gives length of array + lengthLen_int=2, // following 32 bits gives length of array + lengthLen_long=3 // following 64 bits gives length of array (unimpl) + } lengthLen_t; + typedef enum { + typeCode_byte=0, // unknown data type: nItems bytes + typeCode_int=2, // 32-bit integer array: nItems ints + typeCode_long=3, // 64-bit integer array: nItems ints + typeCode_float=5, // 32-bit floating-point array: nItems floats + typeCode_double=6, // 64-bit floating-point array: nItems floats + typeCode_comment=10, // comment/label: nItems byte characters + typeCode_sync=11, // synchronization code + typeCode_pointer=12 // 32 or 64 bit pointer, depending on the machine architecture + } typeCode_t; + void fieldHeader(typeCode_t typeCode,int nItems); +public: + PUP_fmt(PUP::er &parent_) + :PUP::wrap_er(parent_,PUP::er::IS_COMMENTS) {} + + virtual void comment(const char *message); + virtual void synchronize(unsigned int m); + virtual void bytes(void *p,size_t n,size_t itemSize,PUP::dataType t); + virtual void pup_buffer(void *&p,size_t n,size_t itemSize,PUP::dataType t); + virtual void pup_buffer(void *&p,size_t n, size_t itemSize, PUP::dataType t, std::function allocate, std::function deallocate); +}; + diff --git a/src/conv-ccs/ccs-client.C b/src/conv-ccs/ccs-client.C new file mode 100644 index 0000000..e0395ed --- /dev/null +++ b/src/conv-ccs/ccs-client.C @@ -0,0 +1,387 @@ +/* +Converse Client-Server: + Lets you execute, from an arbitrary program on the network, +pre-registered "handlers" in a running converse program. +Also allows you to recv replies from the handlers. +All requests and replies consist of user-defined binary data. + + This file provides the client interface. + +CCS Protocol spec: + + A CCS request message asks a running Converse program to +execute a pre-registered "handler" routine. You send the +request directly to conv-host's CCS server port. +The request, with header, has the following format on the +network: +Ccs Message---------------------------------------------- + /--CcsMessageHeader--------------------------- ^ + | 4 bytes | Message data length d ^ | + | 4 bytes | Dest. processor number | | + | | (big-endian binary integers) | 40+d bytes + +----------------------------------- 40 bytes | + |32 bytes | CCS Handler name | | + | | (ASCII, Null-terminated) v | + \--------------------------------------------- | + d bytes | User data (passed to handler) v +------------------------------------------------------- + + A CCS reply message (if any) comes back on the request socket, +and has only a length header: +CCS Reply ---------------------------------- + | 4 bytes | Message data length d + | | (big-endian binary integer) + +----------------------------------------- + | d bytes | User data +-------------------------------------------- + + */ +#include "ccs-client.h" +#include +#include +#include + +#ifndef CMK_NO_SOCKETS +/*Include the socket and message interface routines + here *whole*, which keeps client linking simple.*/ +#include "sockRoutines.C" +#include "ccs-auth.C" + +#define DEBUGF(x) /*printf x*/ + +/*Parse list of nodes given to us by conv-host. +*/ +static void parseInfo(CcsServer *svr,const void *data) +{ + /*Data conv-host sends us is just a big list of integers*/ + const ChMessageInt_t *d=(const ChMessageInt_t *)data; + int i,index=0; /*Current offset in above array*/ + + svr->numNodes=ChMessageInt(d[index++]); + svr->numProcs = (int *) malloc(svr->numNodes * sizeof(int)); + svr->numPes = 0; + for(i=0;inumNodes;i++) + svr->numPes+=svr->numProcs[i]=ChMessageInt(d[index++]); +} + +static void printSvr(CcsServer *svr) +{ + char ipBuf[200]; + int i; + DEBUGF(("hostIP: %s\n", skt_print_ip(ipBuf,sizeof(ipBuf),svr->hostIP))); + DEBUGF(("hostPort: %d\n", svr->hostPort)); + DEBUGF(("authentication: %d\n", svr->isAuth)); + DEBUGF(("replyFd: %d\n", svr->replyFd)); + DEBUGF(("numNodes: %d\n", svr->numNodes)); + DEBUGF(("numPes: %d\n", svr->numPes)); + for(i=0;inumNodes;i++) { + DEBUGF(("Node[%d] has %d processors\n",i, svr->numProcs[i])); + } +} + +/* Authenticate with this CCS server. + */ +static const char *CcsImpl_authInit(SOCKET fd,CcsServer *svr) +{ + struct { + unsigned char type[4]; + ChMessageInt_t s1; + } request; + int s1; + ChMessageInt_t s2; + SHA1_hash_t s2hash; + struct { + SHA1_hash_t s1hash; + ChMessageInt_t clientID; + ChMessageInt_t clientSalt; + } reply; + if (fd==-1) + return "ERROR> Contacting server"; + request.type[0]=0x80; /*SHA-1 authentication*/ + request.type[1]=0x00; /*Version 0*/ + request.type[2]=0x01; /*Request for salt*/ + request.type[3]=svr->level; /*Security level*/ + s1=CCS_RAND_next(&svr->rand); + request.s1=ChMessageInt_new(s1); + if (-1==skt_sendN(fd,&request,sizeof(request))) + return "ERROR> AuthInit request send"; + if (-1==skt_recvN(fd,&s2,sizeof(s2))) + return "ERROR> AuthInit challenge recv"; + CCS_AUTH_hash(&svr->key,ChMessageInt(s2),NULL,&s2hash); + if (-1==skt_sendN(fd,&s2hash,sizeof(s2hash))) + return "ERROR> AuthInit challenge reply send"; + if (-1==skt_recvN(fd,&reply,sizeof(reply))) + return "ERROR> AuthInit final recv (authentication failure?)"; + if (CCS_AUTH_differ(&svr->key,s1,NULL,&reply.s1hash)) + return "ERROR> AuthInit server key does not match"; + + svr->clientID=ChMessageInt(reply.clientID); + svr->clientSalt=ChMessageInt(reply.clientSalt); + return 0; +} + + +/** + * Converse Client-Server Module: Client Side + */ + +int CcsConnect(CcsServer *svr, const char *host, int port,const CcsSec_secretKey *key){ + return CcsConnectWithTimeout(svr, host, port, key, 120); +} + +int CcsConnectWithTimeout(CcsServer *svr, const char *host, int port, + const CcsSec_secretKey *key, int timeout) +{ + skt_init(); + return CcsConnectIpWithTimeout(svr,skt_lookup_ip(host),port,key, timeout); +} + +int CcsConnectIp(CcsServer *svr, skt_ip_t ip, int port,const CcsSec_secretKey *key){ + return CcsConnectIpWithTimeout(svr, ip, port, key, 120); +} + +int CcsConnectIpWithTimeout(CcsServer *svr, skt_ip_t ip, int port, + const CcsSec_secretKey *key, int timeout) +{ + int msg_len;void *msg_data;/*Reply message*/ + skt_init(); + svr->hostIP = ip; + svr->hostPort = port; + svr->replyFd=INVALID_SOCKET; + + svr->clientID=svr->clientSalt=-1; + if (key==NULL) + svr->isAuth=0; + else + { /*Authenticate with server*/ + SOCKET fd; + const char *err; + svr->isAuth=1; + svr->level=0; /*HACK: hardcoded at security level 0*/ + svr->key=*key; + CCS_RAND_new(&svr->rand); + fd=skt_connect(svr->hostIP, svr->hostPort,timeout); + + if (NULL!=(err=CcsImpl_authInit(fd,svr))) { + fprintf(stderr,"CCS Client error> %s\n",err); + skt_close(fd); + return -1; + } + skt_close(fd); + } + + /*Request the parallel machine's node info*/ + if(CcsSendRequestWithTimeout(svr,"ccs_getinfo",0,0,NULL,timeout) == -1){ + fprintf(stderr,"CCS Client Not Alive\n"); + return -1; + } + + /*Wait for conv-host to get back to us*/ + DEBUGF(("Waiting for conv-host to call us back...\n")); + + if(CcsRecvResponseMsg(svr,&msg_len,&msg_data,timeout) == -1){ + fprintf(stderr,"CCS Client Not Alive\n"); + return -1; + } + + parseInfo(svr,msg_data); + free(msg_data); + + /**/ printSvr(svr);/**/ + return 0; +} + +int CcsNumNodes(CcsServer *svr) +{ + return svr->numNodes; +} + +int CcsNumPes(CcsServer *svr) +{ + return svr->numPes; +} + +int CcsNodeFirst(CcsServer *svr, int node) +{ + int retval=0,i; + for(i=0;inumProcs[node]; + } + return retval; +} + +int CcsNodeSize(CcsServer *svr,int node) +{ + return svr->numProcs[node]; +} + +int CcsSendRequestGeneric(CcsServer *svr, const char *hdlrID, int pe, int *pes, int size, const void *msg, int timeout) { + const void *bufs[4]; int lens[4]; int nBuffers=0; + CcsMessageHeader hdr;/*CCS request header*/ + struct { /*CCS Authentication header*/ + unsigned char type[4]; + ChMessageInt_t clientID; + ChMessageInt_t replySalt; + SHA1_hash_t hash; + } auth; + + hdr.len=ChMessageInt_new(size); + hdr.pe=ChMessageInt_new(pe); + strncpy(hdr.handler,hdlrID,CCS_HANDLERLEN); + + if (svr->replyFd!=-1) skt_close(svr->replyFd); /*We'll never ask for reply*/ + + /*Connect to conv-host, and send the message */ + svr->replyFd=skt_connect(svr->hostIP, svr->hostPort,timeout); + if (svr->replyFd==-1) return -1; + + if (svr->isAuth==1) + {/*Authenticate*/ + auth.type[0]=0x80; /*SHA-1 authentication*/ + auth.type[1]=0x00; /*Version 0*/ + auth.type[2]=0x00; /*Ordinary message*/ + auth.type[3]=svr->level; /*Security level*/ + auth.clientID=ChMessageInt_new(svr->clientID); + svr->replySalt=CCS_RAND_next(&svr->rand); + auth.replySalt=ChMessageInt_new(svr->replySalt); + CCS_AUTH_hash(&svr->key,svr->clientSalt++, + &hdr,&auth.hash); + /*Send the authentication header*/ + bufs[nBuffers]=&auth; lens[nBuffers]=sizeof(auth); nBuffers++; + } + /*Send the CCS header*/ + bufs[nBuffers]=&hdr; lens[nBuffers]=sizeof(hdr); nBuffers++; + + /*Send the processors list*/ + if (pe < -1) { + int i; + ChMessageInt_t *pes_nbo = (ChMessageInt_t *)pes; + for (i=0; i<-pe; ++i) pes_nbo[i] = ChMessageInt_new(pes[i]); + bufs[nBuffers]=pes; lens[nBuffers]=-pe*sizeof(ChMessageInt_t); nBuffers++; + } + + /*Send the request data*/ + if (size>0) {bufs[nBuffers]=msg; lens[nBuffers]=size; nBuffers++;} + + if (-1==skt_sendV(svr->replyFd, nBuffers, bufs,lens)) return -1; + DEBUGF(("[%.3f] Request sent\n",CmiWallTimer())); + /*Leave socket open for reply*/ + return 0; +} + +int CcsSendRequest(CcsServer *svr, const char *hdlrID, int pe, int size, const void *msg) { + return CcsSendRequestGeneric(svr, hdlrID, pe, NULL, size, msg, 120); +} + +int CcsSendRequestWithTimeout(CcsServer *svr, const char *hdlrID, int pe, int size, const void *msg, int timeout) { + return CcsSendRequestGeneric(svr, hdlrID, pe, NULL, size, msg, 120); +} + +int CcsSendBroadcastRequest(CcsServer *svr, const char *hdlrID, + int size, const void *msg) { + return CcsSendRequestGeneric(svr, hdlrID, -1, NULL, size, msg, 120); +} + +int CcsSendBroadcastRequestWithTimeout(CcsServer *svr, const char *hdlrID, + int size, const void *msg, int timeout) { + return CcsSendRequestGeneric(svr, hdlrID, -1, NULL, size, msg, timeout); +} + +int CcsSendMulticastRequest(CcsServer *svr, const char *hdlrID, int npes, + int *pes, int size, const void *msg) { + if (npes < 1) { + fprintf(stderr,"CCS multicast: No processor specified\n"); + return -1; + } + if (npes == 1) return CcsSendRequestGeneric(svr, hdlrID, pes[0], NULL, size, msg, 120); + return CcsSendRequestGeneric(svr, hdlrID, -npes, pes, size, msg, 120); +} + +int CcsSendMulticastRequestWithTimeout(CcsServer *svr, const char *hdlrID, int npes, + int *pes, int size, const void *msg, int timeout) { + if (npes < 1) { + fprintf(stderr,"CCS multicast: No processor specified\n"); + return -1; + } + if (npes == 1) return CcsSendRequestGeneric(svr, hdlrID, pes[0], NULL, size, msg, timeout); + return CcsSendRequestGeneric(svr, hdlrID, -npes, pes, size, msg, timeout); +} + +/*Receive and check server reply authentication*/ +int CcsImpl_recvReplyAuth(CcsServer *svr) +{ + SHA1_hash_t hash; + if (!svr->isAuth) return 0; + if (-1==skt_recvN(svr->replyFd,&hash,sizeof(hash))) return -1; + if (CCS_AUTH_differ(&svr->key,svr->replySalt, + NULL,&hash)) return -1; + return 0; +} + +/*Close the socket to the server. A reply is not expected + */ +int CcsNoResponse(CcsServer *svr) +{ + skt_close(svr->replyFd); + svr->replyFd=-1; + return 0; +} + +/*Receive data back from the server. (Arbitrary length response) +*/ +int CcsRecvResponseMsg(CcsServer *svr, int *size,void **newBuf, int timeout) +{ + ChMessageInt_t netLen; + unsigned int len; + SOCKET fd=svr->replyFd; + if (-1==CcsImpl_recvReplyAuth(svr)) return -1; + if (1!=skt_select1(fd,1000*timeout)) return -1; + if (-1==skt_recvN(fd,&netLen,sizeof(netLen))) return -1; + *size=len=ChMessageInt(netLen); + *newBuf=malloc(len); + if (-1==skt_recvN(fd,*newBuf,len)) return -1; + + /*Close the connection*/ + skt_close(svr->replyFd);svr->replyFd=-1; + return len; +} + +/*Receive data from the server. (In-place receive) +*/ +int CcsRecvResponse(CcsServer *svr, int maxsize, void *recvBuffer,int timeout) +{ + ChMessageInt_t netLen; + unsigned int len; + SOCKET fd=svr->replyFd; + if (-1==CcsImpl_recvReplyAuth(svr)) return -1; + if (1!=skt_select1(fd,1000*timeout)) return -1; + if (-1==skt_recvN(fd,&netLen,sizeof(netLen))) return -1; + len=ChMessageInt(netLen); + DEBUGF(("[%.3f] recv'd reply length\n",CmiWallTimer())); + if (len>maxsize) + {skt_close(fd);return -1;/*Buffer too small*/} + if (-1==skt_recvN(fd,recvBuffer,len)) return -1; + + /*Close the connection*/ + skt_close(svr->replyFd);svr->replyFd=-1; + return len; +} + +int CcsProbe(CcsServer *svr) +{ + return skt_select1(svr->replyFd,0); +} +int CcsProbeTimeout(CcsServer *svr,int timeoutMs) +{ + return skt_select1(svr->replyFd,timeoutMs); +} + +void CcsFinalize(CcsServer *svr) +{ + if (svr->replyFd!=-1) skt_close(svr->replyFd); +} + +#endif + + + diff --git a/src/conv-ccs/ccs-client.h b/src/conv-ccs/ccs-client.h new file mode 100644 index 0000000..738e073 --- /dev/null +++ b/src/conv-ccs/ccs-client.h @@ -0,0 +1,77 @@ +/** + * Converse Client-Server Module: Client Side + */ + +#ifndef __CCS_CLIENT_H_ +#define __CCS_CLIENT_H_ + +#define CMK_NOT_USE_CONVERSE 1 +#include "sockRoutines.h" +#include "ccs-auth.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CcsServer { + /*CCS Server description:*/ + char hostAddr[128]; + skt_ip_t hostIP; + unsigned int hostPort; + + /*Authentication*/ + int isAuth; + int level;/*Security level to ask for*/ + CcsSec_secretKey key; + int clientID,clientSalt; + int replySalt; + CCS_RAND_state rand; + + /*Parallel machine:*/ + int numNodes; + int numPes; + int *numProcs; /*# of processors for each node*/ + + /*Current State:*/ + SOCKET replyFd;/*Socket for replies*/ +} CcsServer; + +/*All routines return -1 on failure*/ +int CcsConnect(CcsServer *svr, const char *host, int port,const CcsSec_secretKey *key); +int CcsConnectWithTimeout(CcsServer *svr, const char *host, int port, + const CcsSec_secretKey *key, int timeout); + +int CcsConnectIp(CcsServer *svr,skt_ip_t ip, int port,const CcsSec_secretKey *key); +int CcsConnectIpWithTimeout(CcsServer *svr,skt_ip_t ip, int port,const CcsSec_secretKey *key, int timeout); + +int CcsSendRequest(CcsServer *svr, const char *hdlrID, int pe, + int size, const void *msg); +int CcsSendRequestWithTimeout(CcsServer *svr, const char *hdlrID, int pe, + int size, const void *msg, int timeout); +int CcsSendBroadcastRequest(CcsServer *svr, const char *hdlrID, + int size, const void *msg); +int CcsSendBroadcastRequestWithTimeout(CcsServer *svr, const char *hdlrID, + int size, const void *msg, int timeout); +int CcsSendMulticastRequest(CcsServer *svr, const char *hdlrID, int npes, + int *pes, int size, const void *msg); +int CcsSendMulticastRequestWithTimeout(CcsServer *svr, const char *hdlrID, int npes, + int *pes, int size, const void *msg, int timeout); + +int CcsNoResponse(CcsServer *svr); +int CcsRecvResponse(CcsServer *svr, + int maxsize, void *recvBuffer, int timeout); +int CcsRecvResponseMsg(CcsServer *svr, + int *retSize,void **newBuf, int timeout); +int CcsNumNodes(CcsServer *svr); +int CcsNumPes(CcsServer *svr); +int CcsNodeFirst(CcsServer *svr, int node); +int CcsNodeSize(CcsServer *svr,int node); +int CcsProbe(CcsServer *svr); +int CcsProbeTimeout(CcsServer *svr,int timeoutMs); +void CcsFinalize(CcsServer *svr); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/src/conv-ccs/ccs-server.C b/src/conv-ccs/ccs-server.C new file mode 100644 index 0000000..20e039e --- /dev/null +++ b/src/conv-ccs/ccs-server.C @@ -0,0 +1,566 @@ +/* +Converse Client/Server: Server-side interface +Orion Sky Lawlor, 9/11/2000, olawlor@acm.org + +This file describes the under-the-hood implementation +of the CCS Server. Here's where Ccs requests from the +network are actually received. +*/ +#include +#include +#include "conv-ccs.h" +#include "ccs-server.h" +#include "ccs-auth.h" + +#if CMK_CCS_AVAILABLE + +/****************** Security and Server Utilities ***************/ +/*Security guard for a CCS server*/ +struct CcsSecMan; +typedef int (*CcsSecMan_allowFn) + (struct CcsSecMan *self,CcsSecAttr *attr); +typedef CcsSec_secretKey * (*CcsSecMan_getKeyFn) + (struct CcsSecMan *self,CcsSecAttr *attr); + +typedef struct CcsSecMan { + CcsSecMan_allowFn allowRequest; + CcsSecMan_getKeyFn getKey; + CcsSec_secretKey * keys[256]; +} CcsSecMan; + + +/*A table of connected clients*/ +typedef struct { + int nClients; + int *clients; /*Gives current salt of corresponding client.*/ + CCS_RAND_state rand; +} CCS_AUTH_clients; + +static void CCS_AUTH_new(CCS_AUTH_clients *cl) +{ + cl->nClients=0; + cl->clients=NULL; + CCS_RAND_new(&cl->rand); +} +static int CCS_AUTH_numClients(CCS_AUTH_clients *cl) { + return cl->nClients; +} +static int CCS_AUTH_addClient(CCS_AUTH_clients *cl) { + int clientNo=cl->nClients++; + if ((clientNo%64)==0) + cl->clients=(int*)realloc(cl->clients,sizeof(int)*(cl->nClients+63)); + cl->clients[clientNo]=CCS_RAND_next(&cl->rand); + return clientNo; +} +static int CCS_AUTH_clientSalt(CCS_AUTH_clients *cl,int clientNo) { + return cl->clients[clientNo]; +} +static void CCS_AUTH_advanceSalt(CCS_AUTH_clients *cl,int clientNo) { + cl->clients[clientNo]++; +} + +/*********************************************** +Send the given data as a CCS reply on the given socket. +Goes to some effort to minimize the number of "sends" +(and hence outgoing TCP packets) by assembling the +header and data in-place. +*/ +static void CcsServer_writeReply(SOCKET fd, + CcsSecMan *securityMgr, + CcsSecAttr *attr, + int replyLen,char *reply) +{ + const void *bufs[3]; int lens[3]; int nBuffers=0; + struct { /*Authentication header*/ + SHA1_hash_t hash; + } aheader; + struct { /*Reply header*/ + ChMessageInt_t len; + } header; + if (attr->auth==1) + { /*Compose a reply SHA-1 hash header*/ + CCS_AUTH_hash(securityMgr->getKey(securityMgr,attr), + ChMessageInt(attr->replySalt),NULL,&aheader.hash); + bufs[nBuffers]=&aheader; lens[nBuffers]=sizeof(aheader); nBuffers++; + } + /*Compose a simple reply header*/ + header.len=ChMessageInt_new(replyLen); + bufs[nBuffers]=&header; lens[nBuffers]=sizeof(header); nBuffers++; + bufs[nBuffers]=reply; lens[nBuffers]=replyLen; nBuffers++; + if (-1==skt_sendV(fd,nBuffers,bufs,lens)) return; + skt_close(fd); +#undef n +} + +/******************************************************** +Authenticate incoming request for a client salt value. +Exchange looks like: + +1.) Client sends request code 0x80 (SHA-1), 0x00 (version 0), +0x01 (create salt), 0xNN (security level); followed by +client challenge (4 bytes, s1) + +2.) Server replies with server challenge (4 bytes, s2) + +3.) Client replies with hashed key & server challenge (20 bytes, s2hash) + +4.) Server replies with hashed key & client challenge (20 bytes, s1hash), +as well as client identifier and initial client salt. (8 bytes total). +*/ +static const char *CcsServer_createSalt(SOCKET fd,CCS_AUTH_clients *cl, + CcsSecMan *securityMgr,CcsSecAttr *attr) +{ + ChMessageInt_t s1; + ChMessageInt_t s2=ChMessageInt_new(CCS_RAND_next(&cl->rand)); + SHA1_hash_t s2hash; + int clientId; + struct { + SHA1_hash_t s1hash; + ChMessageInt_t clientId; + ChMessageInt_t clientSalt; + } reply; + if (-1==skt_recvN(fd,&s1,sizeof(s1))) return "ERROR> CreateSalt challenge recv"; + if (-1==skt_sendN(fd,&s2,sizeof(s2))) return "ERROR> CreateSalt challenge send"; + if (-1==skt_recvN(fd,&s2hash,sizeof(s2hash))) return "ERROR> CreateSalt reply recv"; + if (CCS_AUTH_differ(securityMgr->getKey(securityMgr,attr),ChMessageInt(s2), + NULL,&s2hash)) + return "ERROR> CreateSalt client hash mismatch! (bad password?)"; + CCS_AUTH_hash(securityMgr->getKey(securityMgr,attr),ChMessageInt(s1), + NULL,&reply.s1hash); + clientId=CCS_AUTH_addClient(cl); + reply.clientId=ChMessageInt_new(clientId); + reply.clientSalt=ChMessageInt_new(CCS_AUTH_clientSalt(cl,clientId)); + if (-1==skt_sendN(fd,&reply,sizeof(reply))) return "ERROR> CreateSalt reply send"; + /*HACK: this isn't an error return, and returning an error code + here is wrong; but all we want is to close the socket (not process + a CCS request), and printing out this text isn't a bad idea, so... + */ + return "Created new client"; +} + +/******************* +Grab an ordinary authenticated message off this socket. +The format is: +-4 byte client ID number (returned by createSalt earlier) +-4 byte client challenge (used to by client to authenticate reply) +-20 byte authentication hash code +-Regular CcsMessageHeader +*/ +static const char *CcsServer_SHA1_message(SOCKET fd,CCS_AUTH_clients *cl, + CcsSecMan *securityMgr,CcsSecAttr *attr, + CcsMessageHeader *hdr) +{ + ChMessageInt_t clientNo_net; + int clientNo; + unsigned int salt; + SHA1_hash_t hash; + + /* An ordinary authenticated message */ + if (-1==skt_recvN(fd,&clientNo_net,sizeof(clientNo_net))) + return "ERROR> During recv. client number"; + if (-1==skt_recvN(fd,&attr->replySalt,sizeof(attr->replySalt))) + return "ERROR> During recv. reply salt"; + if (-1==skt_recvN(fd,&hash,sizeof(hash))) + return "ERROR> During recv. authentication hash"; + if (-1==skt_recvN(fd,hdr,sizeof(CcsMessageHeader))) + return "ERROR> During recv. message header"; + clientNo=ChMessageInt(clientNo_net); + + if (clientNo<0 || clientNo>=CCS_AUTH_numClients(cl)) + return "ERROR> Bad client number in SHA-1 request!"; + salt=CCS_AUTH_clientSalt(cl,clientNo); + + /*Check the client's hash*/ + if (CCS_AUTH_differ(securityMgr->getKey(securityMgr,attr),salt, + hdr,&hash)) + return "ERROR> Authentication hash code MISMATCH-- bad or faked key"; + + CCS_AUTH_advanceSalt(cl,clientNo); + return NULL; /*It's a good message*/ +} + +/********************* +Grab a message header from this socket. + */ +static const char *CcsServer_readHeader(SOCKET fd,CCS_AUTH_clients *cl, + CcsSecMan *securityMgr, + CcsSecAttr *attr,CcsMessageHeader *hdr) +{ + /*Read the first bytes*/ + unsigned char len[4]; + if (-1==skt_recvN(fd,&len[0],sizeof(len))) + return "ERROR> During recv. length"; + + /* + Decide what kind of message it is by the high byte of the length field. + */ + if (len[0]<0x20) + { /*Unauthenticated message-- do a security check*/ + attr->auth=0; + attr->level=0; + attr->replySalt=ChMessageInt_new(0); + if (!securityMgr->allowRequest(securityMgr,attr)) + return "ERROR> Unauthenticated request denied at security check"; + /*Request is authorized-- grab the rest of the header*/ + hdr->len=*(ChMessageInt_t *)len; + if (-1==skt_recvN(fd,&hdr->pe,sizeof(hdr->pe))) + return "ERROR> During recv. PE"; + if (-1==skt_recvN(fd,&hdr->handler[0],sizeof(hdr->handler))) + return "ERROR> During recv. handler name"; + return NULL; /*it's a good message*/ + } + else if (len[0]==0x80) + { /*SHA-1 Authenticated request*/ + if (len[1]!=0x00) + return "ERROR> Bad SHA-1 version field!"; + attr->auth=1; + attr->level=len[3];/*Requested security level.*/ + if (!securityMgr->allowRequest(securityMgr,attr)) + return "ERROR> Authenticated request denied at security check"; + + switch(len[2]) { + case 0x00: /*Regular message*/ + return CcsServer_SHA1_message(fd,cl,securityMgr,attr,hdr); + case 0x01: /*Request for salt*/ + return CcsServer_createSalt(fd,cl,securityMgr,attr); + default: + return "ERROR> Bad SHA-1 request field!"; + }; + } + else + return "ERROR> Unknown authentication protocol"; +} + +/******************************************************************* +Default security manager-- without any files, allow +unauthenticated calls at level 0. If securityString is +non-NULL, get keys for higher levels by reading this +text file. +*/ +static int allowRequest_default + (struct CcsSecMan *self,CcsSecAttr *attr) +{ + if (attr->auth==0) + { /*Non-authenticated request-- allow only if *no* password for level zero*/ + return NULL==self->keys[0]; + } else { + /*Authenticated request-- allow only if we *have* a password*/ + return NULL!=self->keys[attr->level]; + } +} + +static CcsSec_secretKey *getKey_default + (struct CcsSecMan *self,CcsSecAttr *attr) +{ + return self->keys[attr->level]; +} + +static void CcsSecMan_make_otp(const char *str,CcsSec_secretKey *key) +{ + int i; + CCS_RAND_state state; + CCS_RAND_new(&state); + i=0; + while (str[i]!=0 && idata)/sizeof(int);i++) { + unsigned int cur=CCS_RAND_next(&state); + key->data[4*i+0]=(unsigned char)(cur>>24); + key->data[4*i+1]=(unsigned char)(cur>>16); + key->data[4*i+2]=(unsigned char)(cur>> 8); + key->data[4*i+3]=(unsigned char)(cur>> 0); + } +} + +static void CcsSecMan_printkey(FILE *out,int level,CcsSec_secretKey *k) +{ + int i; + fprintf(out,"CCS_OTP_KEY> Level %d key: ",level); + for (i=0;idata);i++) + fprintf(out,"%02X",k->data[i]); + fprintf(out,"\n"); +} + +static CcsSecMan *CcsSecMan_default(const char *authFile) +{ + int i; + FILE *secFile; + char line[200]; + CcsSecMan *ret=(CcsSecMan *)malloc(sizeof(CcsSecMan)); + ret->allowRequest=allowRequest_default; + ret->getKey=getKey_default; + for (i=0;i<256;i++) { + ret->keys[i]=NULL; /*Null means no password set-- disallow unless level 0*/ + } + if (authFile==NULL) return ret; + secFile=fopen(authFile,"r"); + if (secFile==NULL) { + fprintf(stderr,"CCS ERROR> Cannot open CCS authentication file '%s'!\n", + authFile); + exit(1); + } + while (NULL!=fgets(line,sizeof(line),secFile)) { + int level; + char key[200]; /*Secret key, in ASCII hex*/ + int nItems=sscanf(line,"%d%s",&level,key); + if (nItems==2 && level>=0 && level<255) { + /*Parse out the secret key*/ + CcsSec_secretKey *k=(CcsSec_secretKey *)malloc(sizeof(CcsSec_secretKey)); + memset(k->data,0,sizeof(CcsSec_secretKey)); + if (isxdigit(key[0]) && isxdigit(key[1])) + CCS_AUTH_makeSecretKey(key,k); + else if (0==strncmp("OTP",key,3)) { + FILE *keyDest=stdout; + CcsSecMan_make_otp(&key[3],k); + CcsSecMan_printkey(keyDest,level,k); + } + else { + fprintf(stderr,"CCS ERROR> Cannot parse key '%s' for level %d from CCS security file '%s'!\n", + key,level,authFile); + exit(1); + } + ret->keys[level]=k; + } + } + fclose(secFile); + return ret; +} + +/*********************************************************/ +#define CCSDBG(x) //printf x + +/*CCS Server state is all stored in global variables. +Since there's only one server, this is ugly but OK. +*/ +static SOCKET ccs_server_fd=SOCKET_ERROR;/*CCS request socket*/ +static CCS_AUTH_clients ccs_clientlist; +static CcsSecMan *globalSecurityMgr; + +/*Make a new Ccs Server socket, on the given port. +Returns the actual port and IP address. +*/ +void CcsServer_new(skt_ip_t *ret_ip,int *use_port,const char *authFile) +{ + char ip_str[200]; + skt_ip_t ip; + unsigned int port=0;if (use_port!=NULL) port=*use_port; + + CCS_AUTH_new(&ccs_clientlist); + globalSecurityMgr=CcsSecMan_default(authFile); + skt_init(); + ip=skt_my_ip(); + ccs_server_fd=skt_server(&port); + printf("ccs: %s\nccs: Server IP = %s, Server port = %u $\n", + CMK_CCS_VERSION, skt_print_ip(ip_str,sizeof(ip_str),ip), port); + fflush(stdout); + if (ret_ip!=NULL) *ret_ip=ip; + if (use_port!=NULL) *use_port=port; +} + +/*Get the Ccs Server socket. This socket can +be added to the rdfs list for calling select(). +*/ +SOCKET CcsServer_fd(void) {return ccs_server_fd;} + +/*Connect to the Ccs Server socket, and +receive a ccs request from the network. +Returns 1 if a request was successfully received. +reqData is allocated with malloc(hdr->len). +*/ +static int req_abortFn(SOCKET skt, int code, const char *msg) { + /*Just ignore bad requests-- indicates a client is messed up*/ + fprintf(stderr,"CCS ERROR> Socket abort during request-- ignoring\n"); + return -1; +} + +static int CcsServer_recvRequestData(SOCKET fd, + CcsImplHeader *hdr,void **reqData) +{ + CcsMessageHeader req;/*CCS header, from requestor*/ + int reqBytes, numPes, destPE; + const char *err; + if (NULL!=(err=CcsServer_readHeader(fd,&ccs_clientlist,globalSecurityMgr, + &hdr->attr,&req))) + { /*Not a regular message-- write error message and return error.*/ + fprintf(stdout,"CCS %s\n",err); + return 0; + } + + /*Fill out the internal CCS header*/ + strncpy(hdr->handler,req.handler,CCS_MAXHANDLER); + hdr->pe=req.pe; + hdr->len=req.len; + hdr->replyFd=ChMessageInt_new(fd); + + /*Is it a multicast?*/ + numPes = 0; + destPE = ChMessageInt(hdr->pe); + if (destPE < -1) numPes = -destPE; + + /*Grab the user data portion of the message*/ + reqBytes=ChMessageInt(req.len) + numPes*sizeof(ChMessageInt_t); + *reqData=(char *)malloc(reqBytes); + if (-1==skt_recvN(fd,*reqData,reqBytes)) { + fprintf(stdout,"CCS ERROR> Retrieving %d message bytes\n",reqBytes); + free(*reqData); + return 0; + } + return 1; +} + +int CcsServer_recvRequest(CcsImplHeader *hdr,void **reqData) +{ + char ip_str[200]; + skt_ip_t ip; + unsigned int port,ret=1; + SOCKET fd; + skt_abortFn old=skt_set_abort(req_abortFn); + + CCSDBG(("CCS Receiving connection...\n")); + fd=skt_accept(ccs_server_fd,&ip,&port); + + CCSDBG(("CCS Connected to IP=%s, port=%d...\n",skt_print_ip(ip_str,sizeof(ip_str),ip),port)); + hdr->attr.ip=ip; + hdr->attr.port=ChMessageInt_new(port); + + if (0==CcsServer_recvRequestData(fd,hdr,reqData)) + { + fprintf(stdout,"During CCS Client IP:port (%s:%d) processing.\n", + skt_print_ip(ip_str,sizeof(ip_str),ip), + port); + skt_close(fd); + ret=0; + } + + CCSDBG(("CCS Ret %d request.\n",ret)); + skt_set_abort(old); + + return ret; +} + +static int reply_abortFn(SOCKET skt, int code, const char *msg) { + /*Just ignore bad replies-- just indicates a client has died*/ + fprintf(stderr,"CCS ERROR> Socket abort during reply-- ignoring\n"); + return -1; +} + +/*Send a Ccs reply down the given socket. +Closes the socket afterwards. +A CcsImplHeader len field equal to 0 means do not send any reply. +*/ +void CcsServer_sendReply(CcsImplHeader *hdr,int repBytes,const void *repData) +{ + int fd=ChMessageInt(hdr->replyFd); + skt_abortFn old; + if (ChMessageInt(hdr->len)==0) { + CCSDBG(("CCS Closing reply socket without a reply.\n")); + skt_close(fd); + return; + } + old=skt_set_abort(reply_abortFn); + CCSDBG(("CCS Sending %d bytes of reply data\n",repBytes)); + CcsServer_writeReply(fd,globalSecurityMgr,&hdr->attr,repBytes,(char *)repData); + skt_close(fd); + CCSDBG(("CCS Reply socket closed.\n")); + skt_set_abort(old); +} + +/*************************************************************************** + * Routines to handle standard out/err forwarding from application to remote + * program connecting through CCS (used by CharmDebug) + ***************************************************************************/ + +#define REDIRECT_STDIO "redirect stdio" +#define FETCH_STDIO "fetch stdio" +char *stdio_buffer = NULL; +int stdio_size = 0; +int stdio_alloc = 0; +int stdio_waiting = 0; +CcsImplHeader stdio_waiting_hdr; + +void write_stdio_duplicate(char* data) { + if (stdio_alloc > 0) { + int size = strlen(data); + + if (stdio_waiting) { + stdio_waiting = 0; + CcsServer_sendReply(&stdio_waiting_hdr,size+1,data); + } + else { + if (size+stdio_size >= stdio_alloc) { + char *newbuf; + stdio_alloc += (size>4096 ? size : 4096); + newbuf = (char*)malloc(stdio_alloc); + memcpy(newbuf, stdio_buffer, stdio_size); + free(stdio_buffer); + stdio_buffer = newbuf; + } + strcpy(&stdio_buffer[stdio_size], data); + stdio_size += size; + } + } +} + +int check_stdio_header(CcsImplHeader *hdr) { + if (strncmp(REDIRECT_STDIO, hdr->handler, strlen(REDIRECT_STDIO))==0) { + /*This is a request to make a duplicate to stdio*/ + if (stdio_alloc == 0) { + stdio_alloc = 4096; + stdio_buffer = (char*)malloc(stdio_alloc); + } + CcsServer_sendReply(hdr,0,0); + } + else if (strncmp(FETCH_STDIO, hdr->handler, strlen(FETCH_STDIO))==0) { + /*Reply with the data loaded until now*/ + if (stdio_size > 0) { + hdr->len = ChMessageInt_new(1); /* fake len to prevent socket closed without reply! */ + CcsServer_sendReply(hdr,stdio_size,stdio_buffer); + stdio_size = 0; + } else { + if (stdio_waiting) { + CcsServer_sendReply(&stdio_waiting_hdr,0,0); + } + stdio_waiting = 1; + stdio_waiting_hdr = *hdr; + stdio_waiting_hdr.len = ChMessageInt_new(1); /* fake len to prevent socket closed without reply! */ + } + } else { + return 0; + } + return 1; +} + +#if !CMK_USE_LRTS_STDIO +#define MAX_PRINT_BUF_SIZE 8192 +int print_fw_handler_idx; + +/* Receives messages passed to processor 0 by all other processors as a + * consequence of prints in debug mode. + */ +void print_fw_handler(char *msg) { + write_stdio_duplicate(msg+CmiReservedHeaderSize); +} + +/* Forward prints to node0 to be buffered and delivered through CCS */ +void print_node0(const char *format, va_list args) { + char buffer[MAX_PRINT_BUF_SIZE]; + int len; + if ((len=vsnprintf(buffer, sizeof(buffer), format, args)) >= MAX_PRINT_BUF_SIZE) CmiAbort("CmiPrintf: printing buffer too long\n"); + if (CmiMyPe() == 0) { + /* We are the print server, just concatenate the printed string */ + write_stdio_duplicate(buffer); + } else { + /* Need to forward the string to processor 0 */ + char* msg = (char *)CmiAlloc(CmiReservedHeaderSize+len+1); + memcpy(msg+CmiReservedHeaderSize, buffer, len+1); + CmiSetHandler(msg,print_fw_handler_idx); + CmiSyncSendAndFree(0,CmiReservedHeaderSize+len+1,msg); + } +} +#endif + +#endif /*CMK_CCS_AVAILABLE*/ + + diff --git a/src/conv-ccs/ccs-server.h b/src/conv-ccs/ccs-server.h new file mode 100644 index 0000000..ca57f9d --- /dev/null +++ b/src/conv-ccs/ccs-server.h @@ -0,0 +1,111 @@ +/* +Converse Client/Server: Server-side interface +Orion Sky Lawlor, 9/13/2000, olawlor@acm.org + +CcsServer routines handle the CCS Server socket, +translate CCS requests and replies into the final +network format, and send/recv the actual requests +and replies. + +Depending on the situation, this code is called from +conv-host.c, or conv-core.c/conv-ccs.C (for +NODE_0_IS_CONVHOST). All the routines in this file +should be called from within only one machine&program-- +the CCS server. That is, you can't receive a request +on one machine, send the request to another machine, +and send the response from the new machine (because +the reply socket is for the original machine). +*/ + +#ifndef CCS_SERVER_H +#define CCS_SERVER_H + +#include "sockRoutines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if CMK_CCS_AVAILABLE + +/*Security attributes for a CCS request*/ +typedef struct { + skt_ip_t ip;/*Source machine (or firewall)*/ + ChMessageInt_t port; + ChMessageInt_t replySalt;/*Salt value for reply hash*/ + unsigned char auth;/*1-- message authenticated; 0-- no authentication*/ + unsigned char level;/*Security level-- 0 to 255*/ +} CcsSecAttr; + +/*Used within CCS implementation to identify requestor*/ +#define CCS_MAXHANDLER 32 /*Longest possible handler name*/ +typedef struct { + CcsSecAttr attr; /*Source information*/ + char handler[CCS_MAXHANDLER];/*Handler name for message to follow*/ + ChMessageInt_t pe;/*Dest. processor # (global numbering)*/ + ChMessageInt_t replyFd;/*Send reply back here*/ + ChMessageInt_t len;/*Bytes of message data to follow*/ +} CcsImplHeader; + +/********* CCS Implementation (not in ccs-server.C) ********/ +/*Deliver this request data to the appropriate PE. */ +void CcsImpl_netRequest(CcsImplHeader *hdr,const void *reqData); + +/*Deliver this reply data to this reply socket. + The data will eventually be sent to the CCS server. +*/ +void CcsImpl_reply(CcsImplHeader *hdr,int repLen,const void *repData); + +/*Send any registered clients kill messages before we exit*/ +void CcsImpl_kill(void); + +/*Convert CCS header & message data into a converse message to handler*/ +char *CcsImpl_ccs2converse(const CcsImplHeader *hdr,const void *data,int *ret_len); + +/******************* ccs-server.C routines ***************/ +/*Make a new Ccs Server socket, on the given port. +Returns the actual port and IP address. +*/ +void CcsServer_new(skt_ip_t *ret_ip,int *use_port,const char *securityFile); + +/*Get the Ccs Server socket. This socket can +be added to the rdfs list for calling select(). +*/ +SOCKET CcsServer_fd(void); + +/*Connect to the Ccs Server socket, and +receive a ccs request from the network. +Returns 1 if a request was successfully received; +0 otherwise. +reqData is allocated with malloc(hdr->len). +*/ +int CcsServer_recvRequest(CcsImplHeader *hdr,void **reqData); + +/*Send a Ccs reply down the given socket. +Closes the socket afterwards. +*/ +void CcsServer_sendReply(CcsImplHeader *hdr,int repBytes,const void *repData); + +/*No request will be sent through this socket. +Closes it. +*/ +void CcsServer_noReply(CcsImplHeader *hdr); + +#if !CMK_USE_LRTS_STDIO +void print_node0(const char *format, va_list args); +#endif + +#else /*CCS not available*/ + +#define CcsServer_new(i,p) /*empty*/ +#define CcsServer_fd() SOCKET_ERROR +#define CcsServer_recvReq(h,b) 0 +#define CcsServer_sendReply(f,l,d) /*empty*/ +#define CcsServer_noReply(f) /*empty*/ +#define CcsImpl_kill() /*empty*/ +#endif /*CCS available*/ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/conv-ccs/conv-ccs.C b/src/conv-ccs/conv-ccs.C new file mode 100644 index 0000000..d191fe3 --- /dev/null +++ b/src/conv-ccs/conv-ccs.C @@ -0,0 +1,599 @@ +#include +#include +#include +#include + +#include "converse.h" +#include "conv-ccs.h" +#include "ccs-server.h" +#include "sockRoutines.h" +#include "queueing.h" + +#ifdef _WIN32 +# include +# define read _read +#endif + +void CpdEndConditionalDeliver_master(void); + +#if CMK_CCS_AVAILABLE + +int CcsReply(CcsImplHeader *rep,int repLen,const void *repData); + +/***************************************************************************** + * + * Converse Client-Server Functions + * + *****************************************************************************/ + +static void initHandlerRec(CcsHandlerRec *c,const char *name) { + if (strlen(name)>=CCS_MAXHANDLER) + CmiAbort("CCS handler names cannot exceed 32 characters"); + c->name=strdup(name); + c->fn=NULL; + c->fnOld=NULL; + c->userPtr=NULL; + c->mergeFn=NULL; + c->nCalls=0; +} + +#if CMK_CHARM4PY +static void callHandlerRec(CcsHandlerRec *c, int reqLen, const void *reqData) +{ + c->nCalls++; + //structure of message: reserved header + handler name length (up to 32) + data length (up to 1024) + //+handler name + data + //get handler name and length (up to 32) + const char *handlerStr = c->name; + const int handlerStrLen = strlen(handlerStr); + //reserved header name and data + char *cmsg = (char *) CmiAlloc(CmiReservedHeaderSize+2*sizeof(int)+CCS_MAXHANDLER+reqLen); + //handler length (up to 32) + memcpy(cmsg+CmiReservedHeaderSize, &handlerStrLen, sizeof(int)); + //data length (up to 1024) + memcpy(cmsg+CmiReservedHeaderSize+sizeof(int), &reqLen, sizeof(int)); + //handler name (up to 32) + memcpy(cmsg+CmiReservedHeaderSize+2*sizeof(int), handlerStr, handlerStrLen); + //data + memcpy(cmsg+CmiReservedHeaderSize+2*sizeof(int)+CCS_MAXHANDLER, reqData, reqLen); + (c->fnOld)(cmsg); +} + +#else +static void callHandlerRec(CcsHandlerRec *c,int reqLen,const void *reqData) { + c->nCalls++; + if (c->fnOld) + { /* Backward compatability version: + Pack user data into a converse message (cripes! why bother?); + user will delete the message. + */ + char *cmsg = (char *) CmiAlloc(CmiReservedHeaderSize+reqLen); + memcpy(cmsg+CmiReservedHeaderSize, reqData, reqLen); + (c->fnOld)(cmsg); + } + else { /* Pass read-only copy of data straight to user */ + (c->fn)(c->userPtr, reqLen, reqData); + } +} + +#endif + +/*Table maps handler name to CcsHandler object*/ +CpvDeclare(CcsHandlerTable, ccsTab); + +CpvStaticDeclare(CcsImplHeader*,ccsReq);/*Identifies CCS requestor (client)*/ + +void CcsRegisterHandler(const char *name, CmiHandler fn) { + CcsHandlerRec cp; + initHandlerRec(&cp,name); + cp.fnOld=fn; + *(CcsHandlerRec *)CkHashtablePut(CpvAccess(ccsTab),(void *)&cp.name)=cp; +} + +#ifdef CMK_CHARM4PY +void CcsRegisterHandlerExt(const char *ccs_handlername, void *fn) +{ + CcsRegisterHandler(ccs_handlername, (CmiHandler)fn); +} +#endif + +void CcsRegisterHandlerFn(const char *name, CcsHandlerFn fn, void *ptr) { + CcsHandlerRec cp; + initHandlerRec(&cp,name); + cp.fn=fn; + cp.userPtr=ptr; + *(CcsHandlerRec *)CkHashtablePut(CpvAccess(ccsTab),(void *)&cp.name)=cp; +} +CcsHandlerRec *CcsGetHandler(const char *name) { + return (CcsHandlerRec *)CkHashtableGet(CpvAccess(ccsTab),(void *)&name); +} +void CcsSetMergeFn(const char *name, CmiReduceMergeFn newMerge) { + CcsHandlerRec *rec=(CcsHandlerRec *)CkHashtableGet(CpvAccess(ccsTab),(void *)&name); + if (rec==NULL) { + CmiAbort("[%d] CCS: Unknown CCS handler name %s.\n",CmiMyPe(),name); + } + rec->mergeFn=newMerge; + rec->redID=CmiGetGlobalReduction(); +} + +void * CcsMerge_concat(int *size,void *local,void **remote,int n) { + CcsImplHeader *hdr; + int total = *size; + void *reply; + char *ptr; + int i; + for (i=0; ilen); + } + reply = CmiAlloc(total); + memcpy(reply, local, *size); + ((CcsImplHeader*)(((char*)reply)+CmiReservedHeaderSize))->len = ChMessageInt_new(total-CmiReservedHeaderSize-sizeof(CcsImplHeader)); + CmiFree(local); + ptr = ((char*)reply)+*size; + for (i=0; ilen); + memcpy(ptr, ((char*)remote[i])+CmiReservedHeaderSize+sizeof(CcsImplHeader), len); + ptr += len; + } + *size = total; + return reply; +} + +#define SIMPLE_REDUCTION(name, dataType, loop) \ +void * CcsMerge_##name(int *size,void *local,void **remote,int n) { \ + int i, m; \ + CcsImplHeader *hdrLocal = (CcsImplHeader*)(((char*)local)+CmiReservedHeaderSize); \ + int lenLocal = ChMessageInt(hdrLocal->len); \ + int nElem = lenLocal / sizeof(dataType); \ + dataType *ret = (dataType *) (hdrLocal+1); \ + CcsImplHeader *hdr; \ + for (m=0; mlen); \ + value = (dataType *)(hdr+1); \ + CmiAssert(lenLocal == len); \ + for (i=0; ivalue[i]) ret[i]=value[i]) + +#undef SIMPLE_REDUCTION +#undef SIMPLE_POLYMORPH_REDUCTION + +int CcsEnabled(void) +{ + return 1; +} + +int CcsIsRemoteRequest(void) +{ + return CpvAccess(ccsReq)!=NULL; +} + +void CcsCallerId(skt_ip_t *pip, unsigned int *pport) +{ + *pip = CpvAccess(ccsReq)->attr.ip; + *pport = ChMessageInt(CpvAccess(ccsReq)->attr.port); +} + +extern int rep_fw_handler_idx; +int rep_fw_handler_idx; + +CcsDelayedReply CcsDelayReply(void) +{ + CcsDelayedReply ret; + int len = sizeof(CcsImplHeader); + if (ChMessageInt(CpvAccess(ccsReq)->pe) < -1) + len += ChMessageInt(CpvAccess(ccsReq)->pe) * sizeof(int); + ret.hdr = (CcsImplHeader*)malloc(len); + memcpy(ret.hdr, CpvAccess(ccsReq), len); + CpvAccess(ccsReq)=NULL; + return ret; +} + +void CcsSendReply(int replyLen, const void *replyData) +{ + if (CpvAccess(ccsReq)==NULL) + CmiAbort("CcsSendReply: reply already sent!\n"); + CpvAccess(ccsReq)->len = ChMessageInt_new(1); + CcsReply(CpvAccess(ccsReq),replyLen,replyData); + CpvAccess(ccsReq) = NULL; +} + +void CcsSendReplyNoError(int replyLen, const void *replyData) { + if (CpvAccess(ccsReq)==NULL) return; + CcsSendReply(replyLen, replyData); +} + +void CcsSendDelayedReply(CcsDelayedReply d,int replyLen, const void *replyData) +{ + CcsImplHeader *h = d.hdr; + h->len=ChMessageInt_new(1); + CcsReply(h,replyLen,replyData); + free(h); +} + +void CcsNoReply(void) +{ + if (CpvAccess(ccsReq)==NULL) return; + CpvAccess(ccsReq)->len = ChMessageInt_new(0); + CcsReply(CpvAccess(ccsReq),0,NULL); + CpvAccess(ccsReq) = NULL; +} + +void CcsNoDelayedReply(CcsDelayedReply d) +{ + CcsImplHeader *h = d.hdr; + h->len = ChMessageInt_new(0); + CcsReply(h,0,NULL); + free(h); +} + + +/********************************** +_CCS Implementation Routines: + These do the request forwarding and +delivery. +***********************************/ + +/*CCS Bottleneck: + Deliver the given message data to the given +CCS handler. +*/ +void CcsHandleRequest(CcsImplHeader *hdr,const char *reqData) +{ + int reqLen=ChMessageInt(hdr->len); +/*Look up handler's converse ID*/ + char *handlerStr=hdr->handler; + CcsHandlerRec *fn=(CcsHandlerRec *)CkHashtableGet(CpvAccess(ccsTab),(void *)&handlerStr); + // CmiPrintf("[%d] CCS: CCS handler name '%s' requested.\n",CmiMyPe(), hdr->handler); + if (fn==NULL) { + CmiPrintf("[%d] CCS: Unknown CCS handler name '%s' requested. Ignoring...\n" + ,CmiMyPe(), hdr->handler); + CpvAccess(ccsReq)=hdr; + CcsSendReply(0,NULL); /*Send an empty reply to the possibly waiting client*/ + return; + /* CmiAbort("CCS: Unknown CCS handler name.\n");*/ + } + +/* Call the handler */ + CpvAccess(ccsReq)=hdr; +#if CMK_CHARMDEBUG + if (conditionalPipe[1]!=0 && _conditionalDelivery==0) { + /* We are conditionally delivering, the message has been sent to the child, wait for its response */ + int bytes; + if (4==read(conditionalPipe[0], &bytes, 4)) { + char *buf = (char *)malloc(bytes); + if (bytes == read(conditionalPipe[0], buf, bytes)) + { + CcsSendReply(bytes,buf); + free(buf); + } + else + { + free(buf); + CpdEndConditionalDeliver_master(); + } + } else { + /* the pipe has been closed */ + CpdEndConditionalDeliver_master(); + } + } + else +#endif + { + callHandlerRec(fn,reqLen,reqData); + +/*Check if a reply was sent*/ + if (CpvAccess(ccsReq)!=NULL) + CcsSendReply(0,NULL);/*Send an empty reply if not*/ + } +} + +#if ! NODE_0_IS_CONVHOST +/* The followings are necessary to prevent CCS requests to be processed before + * CCS has been initialized. Really it matters only when NODE_0_IS_CONVHOST=0, but + * it doesn't hurt having it in the other case as well */ +static char **bufferedMessages = NULL; +static int CcsNumBufferedMsgs = 0; +#define CCS_MAX_NUM_BUFFERED_MSGS 100 + +void CcsBufferMessage(char *msg) { + CmiPrintf("Buffering CCS message\n"); + CmiAssert(CcsNumBufferedMsgs < CCS_MAX_NUM_BUFFERED_MSGS); + if (CcsNumBufferedMsgs < 0) CmiAbort("Why is a CCS message being buffered now???"); + if (bufferedMessages == NULL) bufferedMessages = (char **)malloc(sizeof(char*)*CCS_MAX_NUM_BUFFERED_MSGS); + bufferedMessages[CcsNumBufferedMsgs] = msg; + CcsNumBufferedMsgs ++; +} +#endif + +/*Unpacks request message to call above routine*/ +int _ccsHandlerIdx = 0;/*Converse handler index of routine req_fw_handler*/ + +void req_fw_handler(char *msg); + +void CcsReleaseMessages(void) { +#if ! NODE_0_IS_CONVHOST + if (CcsNumBufferedMsgs > 0) { + int i; + //CmiPrintf("CCS: %d messages released\n",CcsNumBufferedMsgs); + for (i=0; ilen); + int destPE = ChMessageInt(hdr->pe); + int len; + char *msg; + if (destPE < -1) reqLen -= destPE*sizeof(int); + len=CmiReservedHeaderSize+sizeof(CcsImplHeader)+reqLen; + msg=(char *)CmiAlloc(len); + memcpy(msg+CmiReservedHeaderSize,hdr,sizeof(CcsImplHeader)); + memcpy(msg+CmiReservedHeaderSize+sizeof(CcsImplHeader),data,reqLen); + if (ret_len!=NULL) *ret_len=len; + if (_ccsHandlerIdx != 0) { + CmiSetHandler(msg, _ccsHandlerIdx); + return msg; + } else { +#if NODE_0_IS_CONVHOST + CmiAbort("Why do we need to buffer messages when node 0 is Convhost?"); +#else + CcsBufferMessage(msg); +#endif + return NULL; + } +} + +/*Receives reply messages passed up from +converse to node 0.*/ +static void rep_fw_handler(char *msg) +{ + int len; + char *r=msg+CmiReservedHeaderSize; + CcsImplHeader *hdr=(CcsImplHeader *)r; + r+=sizeof(CcsImplHeader); + len=ChMessageInt(hdr->len); + CcsImpl_reply(hdr,len,r); + CmiFree(msg); +} + +#if NODE_0_IS_CONVHOST +/************** NODE_0_IS_CONVHOST *********** +Non netlrts- versions of charm++ are run without a +(real) conv-host program. This is fine, except +CCS clients connect via conv-host; so for CCS +on non-netlrts- versions of charm++, node 0 carries +out the CCS forwarding normally done in conv-host. + +CCS works by listening to a TCP connection on a +port-- the Ccs server socket. A typical communcation +pattern is: + +1.) Random program (CCS client) from the net +connects to the CCS server socket and sends +a CCS request. + +2.) Node 0 forwards the request to the proper +PE as a regular converse message (built in CcsImpl_netReq) +for CcsHandleRequest. + +3.) CcsHandleRequest looks up the user's pre-registered +CCS handler, and passes the user's handler the request data. + +4.) The user's handler calls CcsSendReply with some +reply data; OR finishes without calling CcsSendReply, +in which case CcsHandleRequest does it. + +5.) CcsSendReply forwards the reply back to node 0, +which sends the reply back to the original requestor, +on the (still-open) request socket. + */ + +/** +Send a Ccs reply back to the requestor, down the given socket. +Since there is no conv-host, node 0 does all the CCS +communication-- this means all requests come to node 0 +and are forwarded out; all replies are forwarded back to node 0. + +Note: on Net- versions, CcsImpl_reply is implemented in machine.C +*/ +void CcsImpl_reply(CcsImplHeader *rep,int repLen,const void *repData) +{ + const int repPE=0; + rep->len=ChMessageInt_new(repLen); + if (CmiMyPe()==repPE) { + /*Actually deliver reply data*/ + CcsServer_sendReply(rep,repLen,repData); + } else { + /*Forward data & socket # to the replyPE*/ + int len=CmiReservedHeaderSize+ + sizeof(CcsImplHeader)+repLen; + char *msg = (char *)CmiAlloc(len); + char *r=msg+CmiReservedHeaderSize; + *(CcsImplHeader *)r=*rep; r+=sizeof(CcsImplHeader); + memcpy(r,repData,repLen); + CmiSetHandler(msg,rep_fw_handler_idx); + CmiSyncSendAndFree(repPE,len,msg); + } +} + +/*No request will be sent through this socket. +Closes it. +*/ +/*void CcsImpl_noReply(CcsImplHeader *hdr) +{ + int fd=ChMessageInt(hdr->replyFd); + skt_close(fd); +}*/ + +/** + * This is the entrance point of a CCS request into the server. + * It is executed only on proc 0, and it forwards the request to the appropriate PE. + */ +void CcsImpl_netRequest(CcsImplHeader *hdr,const void *reqData) +{ + char *msg; + int len,repPE=ChMessageInt(hdr->pe); + if (repPE<=-CmiNumPes() || repPE>=CmiNumPes()) { + /*Treat out of bound values as errors. Helps detecting bugs*/ + if (repPE==-CmiNumPes()) CmiPrintf("Invalid processor index in CCS request: are you trying to do a broadcast instead?"); + else CmiPrintf("Invalid processor index in CCS request."); + CpvAccess(ccsReq)=hdr; + CcsSendReply(0,NULL); /*Send an empty reply to the possibly waiting client*/ + return; + } + + msg=CcsImpl_ccs2converse(hdr,reqData,&len); + if (repPE >= 0) { + /* The following %CmiNumPes() follows the assumption that in BigSim the mapping is round-robin */ + //CmiPrintf("CCS message received for %d\n",repPE); + CmiSyncSendAndFree(repPE%CmiNumPes(),len,msg); + } else if (repPE == -1) { + /* Broadcast to all processors */ + //CmiPrintf("CCS broadcast received\n"); + CmiSyncSendAndFree(0,len,msg); + } else { + /* Multicast to -repPE processors, specified right at the beginning of reqData (as a list of pes) */ + int firstPE = ChMessageInt(*(ChMessageInt_t*)reqData); + /* The following %CmiNumPes() follows the assumption that in BigSim the mapping is round-robin */ + //CmiPrintf("CCS multicast received\n"); + CmiSyncSendAndFree(firstPE%CmiNumPes(),len,msg); + } +} + +/* +We have to run a CCS server socket here on +node 0. To keep the speed impact minimal, +we only probe for new connections (with CcsServerCheck) +occasionally. + */ +#include +#include "ccs-server.C" /*Include implementation here in this case*/ +#include "ccs-auth.C" + +/*Check for ready Ccs messages:*/ +void CcsServerCheck(void) +{ + while (1==skt_select1(CcsServer_fd(),0)) { + CcsImplHeader hdr; + void *data; + /* printf("Got CCS connect...\n"); */ + if (CcsServer_recvRequest(&hdr,&data)) + {/*We got a network request*/ + /* printf("Got CCS request...\n"); */ + if (! check_stdio_header(&hdr)) { + CcsImpl_netRequest(&hdr,data); + } + free(data); + } + } +} + +#endif /*NODE_0_IS_CONVHOST*/ + +int _isCcsHandlerIdx(int hIdx) { + if (hIdx==_ccsHandlerIdx) return 1; + if (hIdx==rep_fw_handler_idx) return 1; + return 0; +} + +void CcsBuiltinsInit(char **argv); + +int cmiArgDebugFlag; // Value is 0, unless reset in ConverseCommonInit +CpvDeclare(char *, displayArgument); +CpvCExtern(int, cpdSuspendStartup); +CpvDeclare(int, cpdSuspendStartup); + +void CcsInit(char **argv) +{ + CpvInitialize(CkHashtable_c, ccsTab); + CpvAccess(ccsTab) = CkCreateHashtable_string(sizeof(CcsHandlerRec),5); + CpvInitialize(CcsImplHeader *, ccsReq); + CpvAccess(ccsReq) = NULL; + CmiAssignOnce(&_ccsHandlerIdx,CmiRegisterHandler((CmiHandler)req_fw_handler)); + CpvInitialize(char *, displayArgument); + CpvInitialize(int, cpdSuspendStartup); + CpvAccess(displayArgument) = NULL; + CpvAccess(cpdSuspendStartup) = 0; + + CcsBuiltinsInit(argv); + + CmiAssignOnce(&rep_fw_handler_idx, CmiRegisterHandler((CmiHandler)rep_fw_handler)); +#if NODE_0_IS_CONVHOST +#if !CMK_USE_LRTS_STDIO + CmiAssignOnce(&print_fw_handler_idx, CmiRegisterHandler((CmiHandler)print_fw_handler)); +#endif + { + int ccs_serverPort=0; + char *ccs_serverAuth=NULL; + + if (CmiGetArgFlagDesc(argv,"++server", "Create a CCS server port") | + CmiGetArgIntDesc(argv,"++server-port",&ccs_serverPort, "Listen on this TCP/IP port number") | + CmiGetArgStringDesc(argv,"++server-auth",&ccs_serverAuth, "Use this CCS authentication file")) + if (CmiMyPe()==0) + {/*Create and occasionally poll on a CCS server port*/ + CcsServer_new(NULL,&ccs_serverPort,ccs_serverAuth); + CcdCallOnConditionKeep(CcdPERIODIC,(CcdCondFn)CcsServerCheck,NULL); + } + } +#endif + /* if in parallel debug mode i.e ++cpd, freeze */ + if (cmiArgDebugFlag) + { + if (CmiGetArgStringDesc(argv, "+DebugDisplay",&(CpvAccess(displayArgument)), "X display for gdb used only in cpd mode")) + { + if (CpvAccess(displayArgument) == NULL) + CmiPrintf("WARNING> NULL parameter for +DebugDisplay\n***"); + } + else if (CmiMyPe() == 0) + { + /* only one processor prints the warning */ + CmiPrintf("WARNING> x term for gdb needs to be specified as +DebugDisplay by debugger\n***\n"); + } + + if (CmiGetArgFlagDesc(argv, "+DebugSuspend", "Suspend execution at beginning of program")) { +#if CMK_SMP + if(!CmiInCommThread()) CpvAccess(cpdSuspendStartup) = 1; +#else + CpvAccess(cpdSuspendStartup) = 1; +#endif + // CmiPrintf("[%d] set SuspendStartup %d\n",CmiMyPe(), CpvAccess(cpdSuspendStartup)); + } + } + + CcsReleaseMessages(); +} + +#endif /*CMK_CCS_AVAILABLE*/ + diff --git a/src/conv-ccs/conv-ccs.h b/src/conv-ccs/conv-ccs.h new file mode 100644 index 0000000..451c42b --- /dev/null +++ b/src/conv-ccs/conv-ccs.h @@ -0,0 +1,121 @@ +/*This file describes the CCS Server-side handler +interface. A CCS handler is just a CMI handler, +but it can use the CcsSendReply function. +*/ + +#ifndef CONV_CCS_H +#define CONV_CCS_H + +#include "converse.h" /* for CMK_CCS_AVAILABLE and CmiHandler */ +#include "sockRoutines.h" +#include "ccs-server.h" /*for CcsSecAttr*/ + +#include "ckhashtable.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/******* Converse Client Server *****/ + +#define CMK_CCS_VERSION "2" + +extern int _ccsHandlerIdx; + +#if CMK_CCS_AVAILABLE + +typedef CkHashtable_c CcsHandlerTable; +CpvExtern(CcsHandlerTable, ccsTab); + +typedef struct CcsDelayedReply_struct { + CcsImplHeader *hdr; +} CcsDelayedReply; + +typedef void (*CcsHandlerFn)(void *userPtr,int reqLen,const void *reqData); + +/* Includes all information stored about a single CCS handler. */ +typedef struct CcsHandlerRec { + const char *name; /*Name passed over socket*/ + CmiHandler fnOld; /*Old converse-style handler, or NULL if new-style*/ + CcsHandlerFn fn; /*New-style handler function, or NULL if old-style*/ + void *userPtr; + CmiReduceMergeFn mergeFn; /*Merge function used for bcast requests*/ + int nCalls; /* Number of times handler has been executed*/ + CmiUInt2 redID; /*Reduction ID to be used with CmiListReduce*/ +} CcsHandlerRec; + +/** + * Backward compatability routine: register a regular converse-style handler + * to receive CCS requests. The requests will arrive as a Converse message, + * with a (useless) converse header. + */ +void CcsRegisterHandler(const char *ccs_handlername, CmiHandler fn); + +#ifdef CMK_CHARM4PY +void CcsRegisterHandlerExt(const char *ccs_handlername, void *fn); +#endif + +CcsHandlerRec *CcsGetHandler(const char *name); + +/** + * Register a real Ccs handler function to receive these CCS requests. + * The requests will arrive as a flat, readonly buffer. + */ +void CcsRegisterHandlerFn(const char *ccs_handlername, CcsHandlerFn fn, void *userPtr); + +/** + * Set the merging function for this CCS handler to newMerge. + */ +void CcsSetMergeFn(const char *name, CmiReduceMergeFn newMerge); +/* A few standard functions for merging CCS messages */ +#define SIMPLE_REDUCTION(name) void * CcsMerge_##name(int *size,void *local,void **remote,int n) +#define SIMPLE_POLYMORPH_REDUCTION(nameBase) \ + SIMPLE_REDUCTION(nameBase##_int); \ + SIMPLE_REDUCTION(nameBase##_float); \ + SIMPLE_REDUCTION(nameBase##_double) +SIMPLE_REDUCTION(concat); +SIMPLE_REDUCTION(logical_and); +SIMPLE_REDUCTION(logical_or); +SIMPLE_REDUCTION(bitvec_and); +SIMPLE_REDUCTION(bitvec_and); +SIMPLE_POLYMORPH_REDUCTION(sum); +SIMPLE_POLYMORPH_REDUCTION(product); +SIMPLE_POLYMORPH_REDUCTION(max); +SIMPLE_POLYMORPH_REDUCTION(min); +#undef SIMPLE_REDUCTION +#undef SIMPLE_POLYMORPH_REDUCTION + +void CcsReleaseMessages(); +void CcsInit(char **argv); +int CcsEnabled(void); +int CcsIsRemoteRequest(void); +void CcsCallerId(skt_ip_t *pip, unsigned int *pport); +void CcsSendReply(int replyLen, const void *replyData); +void CcsSendReplyNoError(int replyLen, const void *replyData); +CcsDelayedReply CcsDelayReply(void); +void CcsSendDelayedReply(CcsDelayedReply d,int replyLen, const void *replyData); +void CcsNoReply(); +void CcsNoDelayedReply(CcsDelayedReply d); + +#else +typedef void *CcsDelayedReply; +#define CcsReleaseMessages() /*empty*/ +#define CcsInit(argv) /*empty*/ +#define CcsRegisterHandler(x,y) 0 +#define CcsRegisterHandlerFn(x,y,p) 0 +#define CcsSetMergeFn(x,y) 0 +#define CcsEnabled() 0 +#define CcsIsRemoteRequest() 0 +#define CcsCallerId(x,y) /*empty*/ +#define CcsDelayReply() 0 +#define CcsSendReply(s,r) /*empty*/ +#define CcsSendReplyNoError(s,r) /*empty*/ +#define CcsSendDelayedReply(d,s,r); +#define CcsNoReply() /*empty*/ +#define CcsNoDelayedReply(d) /*empty*/ +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/conv-ccs/middle-ccs.C b/src/conv-ccs/middle-ccs.C new file mode 100644 index 0000000..1833b58 --- /dev/null +++ b/src/conv-ccs/middle-ccs.C @@ -0,0 +1,221 @@ +#include +#include "middle.h" + +#include "ccs-server.h" +#include "conv-ccs.h" + +#ifdef _WIN32 +# include +# include +# include +# define write _write +# define getpid _getpid +#endif + +#if CMK_CCS_AVAILABLE +void CcsHandleRequest(CcsImplHeader *hdr,const char *reqData); + +void req_fw_handler(char *msg) +{ + int offset = CmiReservedHeaderSize + sizeof(CcsImplHeader); + CcsImplHeader *hdr = (CcsImplHeader *)(msg+CmiReservedHeaderSize); + + int destPE = (int)ChMessageInt(hdr->pe); + // CmiPrintf("[%d] req_fw_handler got msg for pe %d\n",CmiMyPe(),destPE); + if (CmiMyPe() == 0 && destPE == -1) { + /* Broadcast message to all other processors */ + int len=CmiReservedHeaderSize+sizeof(CcsImplHeader)+ChMessageInt(hdr->len); + CmiSyncBroadcast(len, msg); + } + else if (destPE < -1) { + /* Multicast the message to your children */ + int len=CmiReservedHeaderSize+sizeof(CcsImplHeader)+ChMessageInt(hdr->len)-destPE*sizeof(ChMessageInt_t); + int index, child, i; + int *pes = (int*)(msg+CmiReservedHeaderSize+sizeof(CcsImplHeader)); + ChMessageInt_t *pes_nbo = (ChMessageInt_t *)pes; + offset -= destPE * sizeof(ChMessageInt_t); + if (ChMessageInt(pes_nbo[0]) == CmiMyPe()) { + for (index=0; index<-destPE; ++index) pes[index] = ChMessageInt(pes_nbo[index]); + } + for (index=0; index<-destPE; ++index) { + if (pes[index] == CmiMyPe()) break; + } + child = (index << 2) + 1; + for (i=0; i<4; ++i) { + if (child+i < -destPE) { + CmiSyncSend(pes[child+i], len, msg); + } + } + } + +#if CMK_SMP + // Charmrun gave us a message that is being handled on the rank 0 CCS PE + // i.e., PE 0, but needs to get forwarded to the real PE destination + if(destPE>0 && destPE!=CmiMyPe()) + + { + int len=CmiReservedHeaderSize+sizeof(CcsImplHeader)+ChMessageInt(hdr->len); + CmiSyncSend(destPE, len, msg); + } + else +#endif + { + CcsHandleRequest(hdr, msg+offset); + } + CmiFree(msg); + +} + +extern int rep_fw_handler_idx; +/** + * Decide if the reply is ready to be forwarded to the waiting client, + * or if combination is required (for broadcast/multicast CCS requests. + */ +int CcsReply(CcsImplHeader *rep,int repLen,const void *repData) { + int repPE = (int)ChMessageInt(rep->pe); + if (repPE <= -1) { + /* Reduce the message to get the final reply */ + CcsHandlerRec *fn; + int len=CmiReservedHeaderSize+sizeof(CcsImplHeader)+repLen; + char *msg=(char*)CmiAlloc(len); + char *r=msg+CmiReservedHeaderSize; + char *handlerStr; + rep->len = ChMessageInt_new(repLen); + *(CcsImplHeader *)r=*rep; r+=sizeof(CcsImplHeader); + memcpy(r,repData,repLen); + CmiSetHandler(msg,rep_fw_handler_idx); + handlerStr=rep->handler; + fn=(CcsHandlerRec *)CcsGetHandler(handlerStr); + if (fn->mergeFn == NULL) CmiAbort("Called CCS broadcast with NULL merge function!\n"); + if (repPE == -1) { + /* CCS Broadcast */ + CkReduce(msg, len, fn->mergeFn); + } else { + /* CCS Multicast */ + CmiListReduce(-repPE, (int*)(rep+1), msg, len, fn->mergeFn, fn->redID); + } + } else { + if (_conditionalDelivery == 0) CcsImpl_reply(rep, repLen, repData); + else { + /* We are the child of a conditional delivery, write to the parent the reply */ + if (write(conditionalPipe[1], &repLen, 4) != 4) { + CmiAbort("CCS> writing reply length to parent failed!"); + } + if (write(conditionalPipe[1], repData, repLen) != repLen) { + CmiAbort("CCS> writing reply data to parent failed!"); + } + } + } + return 0; +} + + +/********************************************** + "ccs_getinfo"-- takes no data + Return the number of parallel nodes, and + the number of processors per node as an array + of 4-byte big-endian ints. +*/ + +void ccs_getinfo(char *msg) +{ + int nNode=CmiNumNodes(); + int len=(1+nNode)*sizeof(ChMessageInt_t); + ChMessageInt_t *table=(ChMessageInt_t *)malloc(len); + int n; + table[0]=ChMessageInt_new(nNode); + for (n=0;n +void CpdNotify(int type, ...) { + void *ptr; int integer, i; + pid_t pid=0; + int levels=64; + void *stackPtrs[64]; + void *sl; + va_list list; + va_start(list, type); + switch (type) { + case CPD_ABORT: + CmiPrintf("CPD: %d Abort %s\n",CmiMyPe(), va_arg(list, char*)); + break; + case CPD_SIGNAL: + CmiPrintf("CPD: %d Signal %d\n",CmiMyPe(), va_arg(list, int)); + break; + case CPD_FREEZE: +#if CMK_HAS_GETPID + pid = getpid(); +#endif + CmiPrintf("CPD: %d Freeze %d\n", CmiMyPe(), (int)pid); + break; + case CPD_BREAKPOINT: + CmiPrintf("CPD: %d BP %s\n",CmiMyPe(), va_arg(list, char*)); + break; + case CPD_CROSSCORRUPTION: + ptr = va_arg(list, void*); + integer = va_arg(list, int); + CmiPrintf("CPD: %d Cross %p %d ",CmiMyPe(), ptr, integer); + sl = MemoryToSlot(ptr); + if (sl != NULL) { + int stackLen; void **stackTrace; + stackLen = Slot_StackTrace(sl, &stackTrace); + CmiPrintf("%d %d ",Slot_ChareOwner(sl),stackLen); + for (i=0; i q; + public: + ... + void pup(PUP::er &p) { + PUPn(isBar); + PUPn(x);PUPn(y);PUPn(z); + PUPn(q); + } +}; + +A more complex example is: +class bar { + private: + foo f; + int nArr;//Length of array below + double *arr;//Heap-allocated array + public: + ... + + void pup(PUP::er &p) { + PUPn(f); // <- automatically calls foo::pup + PUPn(nArr); + if (p.isUnpacking()) // <- must allocate array on other side. + { + arr=new double[nArr]; + _MEMCHECK(arr); + } + PUPv(arr,nArr); // <- special syntax for arrays of simple types + } +}; +*/ + +#ifndef __CK_PUP_H +#define __CK_PUP_H + +#include +#include /*<- for "FILE *" */ +#include +#include + +#ifndef __cplusplus +#error "Use pup_c.h for C programs-- pup.h is for C++ programs" +#endif + +#ifdef STANDALONE_PUP +#define CmiAbort(x) \ + { \ + printf(x); \ + abort(); \ + } +#else +#ifndef CHARM_H +#include "converse.h" // <- for CMK_* defines +#endif +#endif + +// We need CkMigrateMessage only to distinguish the migration +// constructor from all other constructors-- the type +// itself has no meaningful fields. +typedef struct { + int is_only_a_name; +} CkMigrateMessage; + +namespace PUP { +class er; // Forward declare for all sorts of things + +/** + * A utility to let classes avoid default construction when they're + * about to be unpacked, by defining a constructor that takes a + * value of type PUP::reconstruct + */ +struct reconstruct {}; +namespace detail { +template ::value> +struct TemporaryObjectHolder {}; +template struct TemporaryObjectHolder { + typename std::remove_cv::type>::type t{ + reconstruct()}; +}; +template struct TemporaryObjectHolder { + typename std::remove_cv::type>::type t{}; +}; +template void operator|(er &p, TemporaryObjectHolder &t) { + p | t.t; +} +} // namespace detail + +#if CMK_LONG_LONG_DEFINED +#define CMK_PUP_LONG_LONG long long +#elif CMK___int64_DEFINED +#define CMK_PUP_LONG_LONG __int64 +#endif + +// Item data types-- these are used to do byte swapping, etc. +typedef enum { + //(this list must exactly match that in PUPer_xlate) + Tchar = 0, + Tshort, + Tint, + Tlong, + Tlonglong, + Tuchar, + Tushort, + Tuint, + Tulong, + Tulonglong, +#if CMK_HAS_INT16 + Tint128, + Tuint128, +#endif + Tfloat, + Tdouble, + Tlongdouble, + Tbool, + Tbyte, + Tsync, + Tpointer, + dataType_last //<- for setting table lengths, etc. +} dataType; + +static inline dataType getXlateDataType(signed char *a) { return Tchar; } +static inline dataType getXlateDataType(char *a) { return Tchar; } +static inline dataType getXlateDataType(short *a) { return Tshort; } +static inline dataType getXlateDataType(int *a) { return Tint; } +static inline dataType getXlateDataType(long *a) { return Tlong; } +static inline dataType getXlateDataType(unsigned char *a) { return Tuchar; } +static inline dataType getXlateDataType(unsigned short *a) { return Tushort; } +static inline dataType getXlateDataType(unsigned int *a) { return Tuint; } +static inline dataType getXlateDataType(unsigned long *a) { return Tulong; } +static inline dataType getXlateDataType(float *a) { return Tfloat; } +static inline dataType getXlateDataType(double *a) { return Tdouble; } +#if CMK_LONG_DOUBLE_DEFINED +static inline dataType getXlateDataType(long double *a) { return Tlongdouble; } +#endif +static inline dataType getXlateDataType(bool *a) { return Tbool; } +#ifndef CMK_PUP_LONG_LONG +static inline dataType getXlateDataType(long long *a) { return Tlonglong; } +static inline dataType getXlateDataType(unsigned long long *a) { + return Tulonglong; +} +#else +static inline dataType getXlateDataType(CMK_PUP_LONG_LONG *a) { + return Tlonglong; +} +static inline dataType getXlateDataType(unsigned CMK_PUP_LONG_LONG *a) { + return Tulonglong; +} +#endif +#if CMK_HAS_INT16 +static inline dataType getXlateDataType(CmiInt16 *a) { return Tint128; } +static inline dataType getXlateDataType(CmiUInt16 *a) { return Tuint128; } +#endif + +// This should be a 1-byte unsigned type +typedef unsigned char myByte; + +// Forward declarations +class er; +class able; + +// Used for out-of-order unpacking +class seekBlock { + enum { maxSections = 3 }; + int secTab[maxSections + 1]; // The start of each seek section + int nSec; // Number of sections; current section # + int secTabOff; // Start of the section table, relative to the seek block + er &p; + bool hasEnded; + +public: + // Constructor + seekBlock(er &Np, int nSections); + // Destructor + ~seekBlock(); + + // Seek to the given section number (0-based, less than nSections) + void seek(int toSection); + // Finish with this seeker (must be called) + void endBlock(void); + + // An evil hack to avoid inheritance and virtual functions among seekers-- + // stores the PUP::er specific block start information. + union { + size_t off; + long loff; + const myByte *cptr; + myByte *ptr; + void *vptr; + } data; +}; + +// The abstract base class: PUP::er. +class er { +private: + er(const er &p); // You don't want to copy PUP::er's. +protected: + unsigned int PUP_er_state; + // You don't want to create raw PUP::er's. + explicit er(unsigned int inType) : PUP_er_state(inType) {} + + /// These state bits describe the PUP::er's direction. + enum { + IS_SIZING = 0x0100, + IS_PACKING = 0x0200, + IS_UNPACKING = 0x0400, + TYPE_MASK = 0xFF00 + }; + +public: + virtual ~er(); //<- does nothing, but might be needed by some child + + // These state bits describe various user-settable properties. This needs to + // be public because it used when PUPers are created from the checkpointing + // and migration code. + enum { + IS_USERLEVEL = + 0x0004, // If set, this is not a migration pup - it's something else. + IS_DELETING = + 0x0008, // If set, C & f90 objects should delete themselves after pup + IS_COMMENTS = 0x0010, // If set, this PUP::er wants comments and sync codes. + IS_CHECKPOINT = + 0x0020, // If set, it is creating or restarting from a checkpoint + IS_MIGRATION = 0x0040 // If set, it is migrating between PEs (e.g. in LB) + }; + + // State queries (exactly one of these will be true) + bool isSizing(void) const { + return (PUP_er_state & IS_SIZING) != 0 ? true : false; + } + bool isPacking(void) const { + return (PUP_er_state & IS_PACKING) != 0 ? true : false; + } + bool isUnpacking(void) const { + return (PUP_er_state & IS_UNPACKING) != 0 ? true : false; + } + const char *typeString() const; + unsigned int getStateFlags(void) const { return PUP_er_state; } + + // This indicates that the pup routine should free memory during packing. + void becomeDeleting(void) { PUP_er_state |= IS_DELETING; } + bool isDeleting(void) const { + return (PUP_er_state & IS_DELETING) != 0 ? true : false; + } + + // This indicates that the pup routine should not call system objects' pups. + void becomeUserlevel(void) { PUP_er_state |= IS_USERLEVEL; } + bool isUserlevel(void) const { + return (PUP_er_state & IS_USERLEVEL) != 0 ? true : false; + } + + bool isCheckpoint(void) const { return (PUP_er_state & IS_CHECKPOINT) != 0; } + + bool isMigration(void) const { return (PUP_er_state & IS_MIGRATION) != 0; } + + bool hasComments(void) const { + return (PUP_er_state & IS_COMMENTS) != 0 ? true : false; + } + + // For single elements, pretend it's an array containing one element + template void operator()(T &v) { (*this)(&v, 1); } + + void operator()(void *&v, void *sig) { (*this)(&v, 1, sig); } + + // For arrays: + template void operator()(T *a, size_t nItems) { + bytes((void *)a, nItems, sizeof(T), getXlateDataType(a)); + } + + // Standard pup_buffer API that calls malloc for allocation on isUnpacking and + // free for deallocation on isPacking + template void pup_buffer(T *&a, size_t nItems) { + pup_buffer((void *&)a, nItems, sizeof(T), getXlateDataType(a)); + } + + // Custom pup_buffer API that calls user provided 'allocate' function for + // allocation on isUnpacking and user provided 'deallocate' function for + // deallocation on isPacking Custom pup_buffer behaves same as the standard + // pup_buffer except for calling custom allocator and deallocator methods + template + void pup_buffer(T *&a, size_t nItems, std::function allocate, + std::function deallocate) { + pup_buffer((void *&)a, nItems, sizeof(T), getXlateDataType(a), allocate, + deallocate); + } + + // For pointers: the last parameter is to make it more difficult to call + //(should not be used in normal code as pointers may loose meaning across + // processor) + void operator()(void **a, size_t nItems, void *pointerSignature) { + (void)pointerSignature; + bytes((void *)a, nItems, sizeof(void *), Tpointer); + } + + // For raw memory (n gives number of bytes) + /* + // pup void * is error-prune, let's avoid it - Gengbin + void operator()(void *a,size_t nBytes) + {bytes((void *)a,nBytes,1,Tbyte);} + */ + + // For allocatable objects (system will new/delete object and call pup + // routine) + void operator()(able **a) { object(a); } + // For pre- or stack-allocated PUP::able objects-- just call object's pup + void operator()(able &a); + + /// A descriptive (but entirely optional) human-readable comment field + virtual void comment(const char *message); + + /// A 32-bit "synchronization marker" (not human readable). + /// Some standard codes are listed under PUP::sync_.... + virtual void synchronize(unsigned int sync); + + /// Insert a synchronization marker and comment into the stream. + /// Only applies if this PUP::er wants comments. + inline void syncComment(unsigned int sync, const char *message = 0) { +#if CMK_ERROR_CHECKING + if (hasComments()) { + synchronize(sync); + if (message) + comment(message); + } +#else + /* empty, to avoid expensive virtual function calls */ +#endif + } + + // Generic bottleneck: pack/unpack n items of size itemSize + // and data type t from p. Desc describes the data item + virtual void bytes(void *p, size_t n, size_t itemSize, dataType t) = 0; + virtual void object(able **a); + + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t) = 0; + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate) = 0; + + virtual size_t size(void) const { return 0; } + + // For seeking (pack/unpack in different orders) + virtual void impl_startSeek(seekBlock &s); /*Begin a seeking block*/ + virtual size_t impl_tell(seekBlock &s); /*Give the current offset*/ + virtual void impl_seek(seekBlock &s, size_t off); /*Seek to the given offset*/ + virtual void impl_endSeek(seekBlock &s); /*End a seeking block*/ + + // See more documentation before PUP_cmiAllocSizer in pup_cmialloc.h + // Must be a CmiAlloced buf while packing + virtual void pupCmiAllocBuf(void **msg) { + (void)msg; + CmiAbort("Undefined PUPer:Did you use PUP_toMem or PUP_fromMem?\n"); + } + + // In case source is not CmiAlloced the size can be passed and any + // user buf can be converted into a cmialloc'ed buf + virtual void pupCmiAllocBuf(void **msg, size_t size) { + (void)msg; + (void)size; + CmiAbort("Undefined PUPer:Did you use PUP_toMem or PUP_fromMem?\n"); + } +}; + +/** + "Sync" codes are an extra channel to encode data in a pup stream, + to indicate higher-order structures in the pup'd objects. + Sync codes must follow this grammar: + -> | | + -> begin ( system) end + -> begin (item )* end + -> begin (index item )* end + This hack is used, e.g., by the debugger. +*/ +enum { + sync_builtin = 0x70000000, // Built-in, standard sync codes begin here + sync_begin = sync_builtin + 0x01000000, // Sync code at start of collection + sync_end = sync_builtin + 0x02000000, // Sync code at end of collection + sync_last_system = + sync_builtin + + 0x09000000, // Sync code at end of "system" portion of object + sync_array_m = + 0x00100000, // Linear-indexed (0..n) array-- use item to separate + sync_list_m = 0x00200000, // Some other collection-- use index and item + sync_object_m = 0x00300000, // Sync mask for general object + + sync_begin_array = sync_begin + sync_array_m, + sync_begin_list = sync_begin + sync_list_m, + sync_begin_object = sync_begin + sync_object_m, + + sync_end_array = sync_end + sync_array_m, + sync_end_list = sync_end + sync_list_m, + sync_end_object = sync_end + sync_object_m, + + sync_item = sync_builtin + 0x00110000, // Sync code for a list or array item + sync_index = + sync_builtin + 0x00120000, // Sync code for index of item in a list + + sync_last +}; + +/************** PUP::er -- Sizer ******************/ +// For finding the number of bytes to pack (e.g., to preallocate a memory +// buffer) +class sizer : public er { +protected: + size_t nBytes; + // Generic bottleneck: n items of size itemSize + virtual void bytes(void *p, size_t n, size_t itemSize, dataType t); + + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate); + +public: + // Write data to the given buffer + sizer(const unsigned int purpose = 0) : er(IS_SIZING | purpose), nBytes(0) { + CmiAssert((purpose & TYPE_MASK) == 0); + } + + // Return the current number of bytes to be packed + size_t size(void) const { return nBytes; } +}; + +template inline size_t size(T &t) { + PUP::sizer p; + p | t; + return p.size(); +} + +/********** PUP::er -- Binary memory buffer pack/unpack *********/ +class mem : public er { // Memory-buffer packers and unpackers +protected: + myByte *origBuf; // Start of memory buffer + myByte *buf; // Memory buffer (stuff gets packed into/out of here) + mem(const unsigned int type, myByte *Nbuf, const unsigned int purpose = 0) + : er(type | purpose), origBuf(Nbuf), buf(Nbuf) { + CmiAssert((purpose & TYPE_MASK) == 0); + } + mem(const mem &p); // You don't want to copy + void operator=(const mem &p); // You don't want to copy + + // For seeking (pack/unpack in different orders) + virtual void impl_startSeek(seekBlock &s); /*Begin a seeking block*/ + virtual size_t impl_tell(seekBlock &s); /*Give the current offset*/ + virtual void impl_seek(seekBlock &s, size_t off); /*Seek to the given offset*/ +public: + // Return the current number of buffer bytes used + size_t size(void) const { return buf - origBuf; } + + inline char *get_current_pointer() const { + return reinterpret_cast(buf); + } + + inline char *get_orig_pointer() const { + return reinterpret_cast(origBuf); + } + + inline void reset() { buf = origBuf; } + + inline void advance(size_t const offset) { buf += offset; } +}; + +// For packing into a preallocated, presized memory buffer +class toMem : public mem { +protected: + // Generic bottleneck: pack n items of size itemSize from p. + virtual void bytes(void *p, size_t n, size_t itemSize, dataType t); + + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate); + +public: + // Write data to the given buffer + toMem(void *Nbuf, const unsigned int purpose = 0) + : mem(IS_PACKING, (myByte *)Nbuf, purpose) {} +}; +template inline void toMemBuf(T &t, void *buf, size_t len) { + PUP::toMem p(buf); + p | t; + if (p.size() != len) + CmiAbort( + "Size mismatch during PUP::toMemBuf!\n" + "This means your pup routine doesn't match during sizing and packing"); +} + +// For unpacking from a memory buffer +class fromMem : public mem { +protected: + // Generic bottleneck: unpack n items of size itemSize from p. + virtual void bytes(void *p, size_t n, size_t itemSize, dataType t); + + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate); + + void pup_buffer_generic(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + bool isMalloc); + +public: + // Read data from the given buffer + fromMem(const void *Nbuf, const unsigned int purpose = 0) + : mem(IS_UNPACKING, (myByte *)Nbuf, purpose) {} +}; +template inline void fromMemBuf(T &t, void *buf, size_t len) { + PUP::fromMem p(buf); + p | t; + if (p.size() != len) + CmiAbort("Size mismatch during PUP::fromMemBuf!\n" + "This means your pup routine doesn't match during packing and " + "unpacking"); +} + +/********** PUP::er -- Binary disk file pack/unpack *********/ +class disk : public er { +protected: + FILE *F; // Disk file to read from/write to + disk(const unsigned int type, FILE *f, const unsigned int purpose = 0) + : er(type | purpose), F(f) { + CmiAssert((purpose & TYPE_MASK) == 0); + } + + disk(const disk &p); // You don't want to copy + void operator=(const disk &p); // You don't want to copy + + // For seeking (pack/unpack in different orders) + virtual void impl_startSeek(seekBlock &s); /*Begin a seeking block*/ + virtual size_t impl_tell(seekBlock &s); /*Give the current offset*/ + virtual void impl_seek(seekBlock &s, size_t off); /*Seek to the given offset*/ +}; + +// For packing to a disk file +class toDisk : public disk { +protected: + // Generic bottleneck: pack n items of size itemSize from p. + virtual void bytes(void *p, size_t n, size_t itemSize, dataType t); + + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate); + + bool error; + +public: + // Write data to the given file pointer + // (must be opened for binary write) + // You must close the file yourself when done. + toDisk(FILE *f, const unsigned int purpose = 0) + : disk(IS_PACKING, f, purpose) { + error = false; + } + bool checkError() { return error; } +}; + +// For unpacking from a disk file +class fromDisk : public disk { +protected: + // Generic bottleneck: unpack n items of size itemSize from p. + virtual void bytes(void *p, size_t n, size_t itemSize, dataType t); + + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate); + +public: + // Read data from the given file pointer + // (must be opened for binary read) + // You must close the file yourself when done. + fromDisk(FILE *f, const unsigned int purpose = 0) + : disk(IS_UNPACKING, f, purpose) {} +}; + +/************** PUP::er -- Text *****************/ +class toTextUtil : public er { +private: + char *cur; /*Current output buffer*/ + size_t maxCount; /*Max length of cur buffer*/ + int level; /*Indentation distance*/ + void beginEnv(const char *type, int n = 0); + void endEnv(const char *type); + char *beginLine(void); + void endLine(void); + +protected: + virtual char * + advance(char *cur) = 0; /*Consume current buffer and return next*/ + toTextUtil(unsigned int inType, char *buf, size_t len); + toTextUtil(const toTextUtil &p); // You don't want to copy + void operator=(const toTextUtil &p); // You don't want to copy +public: + virtual void comment(const char *message); + virtual void synchronize(unsigned int m); + +protected: + virtual void bytes(void *p, size_t n, size_t itemSize, dataType t); + + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate); + + virtual void object(able **a); +}; +/* Return the number of characters, including terminating NULL */ +class sizerText : public toTextUtil { +public: + static constexpr int lineLen = 1000; + +private: + char line[lineLen]; + size_t charCount; /*Total characters seen so far (not including NULL) */ +protected: + virtual char *advance(char *cur); + +public: + sizerText(void); + size_t size(void) const { return charCount + 1; /*add NULL*/ } +}; + +/* Copy data to this C string, including terminating NULL. */ +class toText : public toTextUtil { +private: + char *buf; + size_t charCount; /*Total characters written so far (not including NULL) */ + size_t maxCount; /*Max length of buf*/ +protected: + virtual char *advance(char *cur); + +public: + toText(char *outStr, size_t len); + toText(const toText &p); // You don't want to copy + void operator=(const toText &p); // You don't want to copy + size_t size(void) const { return charCount + 1; /*add NULL*/ } +}; + +class toTextFile : public er { +protected: + FILE *f; + virtual void bytes(void *p, size_t n, size_t itemSize, dataType t); + + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate); + +public: + // Begin writing to this file, which should be opened for ascii write. + // You must close the file yourself when done. + toTextFile(FILE *f_) : er(IS_PACKING), f(f_) {} + toTextFile(const toTextFile &p); // You don't want to copy + void operator=(const toTextFile &p); // You don't want to copy + virtual void comment(const char *message); +}; +class fromTextFile : public er { +protected: + FILE *f; + int readInt(const char *fmt = "%d"); + unsigned int readUint(const char *fmt = "%u"); + CMK_TYPEDEF_INT8 readLongInt(const char *fmt = "%lld"); + double readDouble(void); + + virtual void bytes(void *p, size_t n, size_t itemSize, dataType t); + + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate); + + virtual void parseError(const char *what); + +public: + // Begin writing to this file, which should be opened for ascii read. + // You must close the file yourself when done. + fromTextFile(FILE *f_) : er(IS_UNPACKING), f(f_) {} + fromTextFile(const fromTextFile &p); // You don't want to copy + void operator=(const fromTextFile &p); // You don't want to copy + virtual void comment(const char *message); +}; + +/********** PUP::er -- Heterogenous machine pack/unpack *********/ +// This object describes the data representation of a machine. +class machineInfo { +public: + typedef unsigned char myByte; + myByte magic[4]; // Magic number (to identify machineInfo structs) + myByte version; // 0-- current version + + myByte intBytes[5]; //<- sizeof(char,short,int,long,int128) + myByte intFormat; // 0-- big endian. 1-- little endian. + + myByte floatBytes; //<- sizeof(...) + myByte doubleBytes; + myByte floatFormat; // 0-- big endian IEEE. 1-- little endian IEEE. + + myByte boolBytes; + myByte pointerBytes; + + // myByte padding[1];//Padding to 16 bytes + + // Return true if our magic number is valid. + bool valid(void) const; + // Return true if we differ from the current (running) machine. + bool needsConversion(void) const; + + // Get a machineInfo for the current machine + static const machineInfo ¤t(void); + + void pup(er &p) { + myByte padding; + + p(magic, 4); + p(version); + if (version == 0) + p(intBytes, 4); + else + p(intBytes, 5); + p(intFormat); + p(floatBytes); + p(doubleBytes); + p(floatFormat); + p(boolBytes); + p(pointerBytes); + if (version == 0) + p(padding); + } +}; + +/// "Wrapped" PUP::er: forwards requests to another PUP::er. +class wrap_er : public er { +protected: + er &p; + +public: + wrap_er(er &p_, unsigned int newFlags = 0) + : er(p_.getStateFlags() | newFlags), p(p_) {} + virtual size_t size(void) const { return p.size(); } + + virtual void impl_startSeek(seekBlock &s); /*Begin a seeking block*/ + virtual size_t impl_tell(seekBlock &s); /*Give the current offset*/ + virtual void impl_seek(seekBlock &s, size_t off); /*Seek to the given offset*/ + virtual void impl_endSeek(seekBlock &s); /*End a seeking block*/ +}; + +// For translating some odd disk/memory representation into the +// current machine representation. (We really only need to +// translate during unpack-- "reader makes right".) +class xlater : public wrap_er { +protected: + typedef void (*dataConverterFn)(int N, const myByte *in, myByte *out, + size_t nElem); + + // This table is indexed by dataType, and contains an appropriate + // conversion function to unpack a n-item array of the corresponding + // data type (possibly in-place). + dataConverterFn convertFn[dataType_last]; + // Maps dataType to source machine's dataSize + size_t convertSize[dataType_last]; + void setConverterInt(const machineInfo &m, const machineInfo &cur, + int isUnsigned, int intType, dataType dest); + + // Generic bottleneck: unpack n items of size itemSize from p. + virtual void bytes(void *p, size_t n, size_t itemSize, dataType t); + + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate); + +public: + xlater(const machineInfo &fromMachine, er &fromData); +}; + +/*************** PUP::able support ***************/ +// The base class of system-allocatable objects with pup routines +class able { +public: + // A globally-unique, persistent identifier for an allocatable object + class PUP_ID { + public: + enum { len = 8 }; + unsigned char hash[len]; + PUP_ID() {} + PUP_ID(int val) { + for (int i = 0; i < len; i++) + hash[i] = val; + } + PUP_ID(const char *name) { setName(name); } + void setName(const char *name); // Write name into hash + bool operator==(const PUP_ID &other) const { + for (int i = 0; i < len; i++) + if (hash[i] != other.hash[i]) + return false; + return true; + } + bool operator<(const PUP_ID &other) const { + for (int i = 0; i < len; i++) + if (hash[i] != other.hash[i]) + return hash[i] < other.hash[i]; + return false; // they are equal + } + void pup(er &p) { p((char *)hash, sizeof(unsigned char) * len); } + void pup(er &p) const { p((char *)hash, sizeof(unsigned char) * len); } + }; + +protected: + able() {} + able(CkMigrateMessage *) {} + +public: + virtual ~able(); // Virtual destructor may be needed by some child + + // Constructor function registration: + typedef able *(*constructor_function)(void); + static PUP_ID register_constructor(const char *className, + constructor_function fn); + static constructor_function get_constructor(const PUP_ID &id); + virtual /*PUP::*/ able *clone(void) const; + + // Target methods: + virtual void pup(er &p); + virtual const PUP_ID &get_PUP_ID(void) const = 0; +}; + +template ::value> +struct ptr_helper; + +#define SINGLE_ARG(...) __VA_ARGS__ + +// Declarations which create routines implemeting the | operator. +// Macros to be used inside a class body. +#define PUPable_operator_inside(className) \ + friend inline void operator|(PUP::er &p, className &a) { a.pup(p); } \ + friend inline void operator|(PUP::er &p, className *&a) { \ + PUP::able *pa = a; \ + p(&pa); \ + a = dynamic_cast(pa); \ + } + +// Macros to be used outside a class body. +#define PUPable_operator_outside(className) \ + inline void operator|(PUP::er &p, className &a) { a.pup(p); } \ + inline void operator|(PUP::er &p, className *&a) { \ + PUP::able *pa = a; \ + p(&pa); \ + a = dynamic_cast(pa); \ + } + +// Declarations to include in a PUP::able's body. +// Convenient, but only usable if class is not inside a namespace. +#define PUPable_decl(className) \ + PUPable_decl_inside(className) PUPable_operator_inside(className) + +#define PUPable_decl_template(className) \ + PUPable_decl_inside_template(SINGLE_ARG(className)) \ + PUPable_operator_inside(SINGLE_ARG(className)) + +// PUPable_decl for classes inside a namespace: inside body +#define PUPable_decl_inside(className) \ +private: \ + static PUP::able *call_PUP_constructor(void); \ + static PUP::able::PUP_ID my_PUP_ID; \ + \ +public: \ + virtual const PUP::able::PUP_ID &get_PUP_ID(void) const; \ + static void register_PUP_ID(const char *name); + +#define PUPable_decl_base_template(baseClassName, className) \ + PUPable_decl_inside_base_template(SINGLE_ARG(baseClassName), \ + SINGLE_ARG(className)) \ + PUPable_operator_inside(SINGLE_ARG(className)) + +#define PUPable_decl_inside_template(className) \ +private: \ + static PUP::able *call_PUP_constructor(void) { \ + className *pupobj = new className((CkMigrateMessage *)0); \ + _MEMCHECK(pupobj); \ + return pupobj; \ + } \ + static PUP::able::PUP_ID my_PUP_ID; \ + \ +public: \ + virtual const PUP::able::PUP_ID &get_PUP_ID(void) const { \ + return my_PUP_ID; \ + } \ + static void register_PUP_ID(const char *name) { \ + my_PUP_ID = register_constructor(name, call_PUP_constructor); \ + } + +#define PUPable_decl_inside_base_template(baseClassName, className) \ +private: \ + static PUP::able *call_PUP_constructor(void) { \ + className *pupobj = new className((CkMigrateMessage *)0); \ + _MEMCHECK(pupobj); \ + return pupobj; \ + } \ + static PUP::able::PUP_ID my_PUP_ID; \ + \ +public: \ + virtual const PUP::able::PUP_ID &get_PUP_ID(void) const { \ + return my_PUP_ID; \ + } \ + static void register_PUP_ID(const char *name) { \ + my_PUP_ID = \ + baseClassName::register_constructor(name, call_PUP_constructor); \ + } + +// PUPable_decl for classes inside a namespace: in header at file scope +#define PUPable_decl_outside(className) PUPable_operator_outside(className) + +// PUPable_decl for classes inside a namespace: in header at file scope +#define PUPable_decl_outside_template(templateParameters, className) \ + template \ + inline void operator|(PUP::er &p, className &a) { \ + a.pup(p); \ + } \ + template \ + inline void operator|(PUP::er &p, className *&a) { \ + PUP::able *pa = a; \ + p(&pa); \ + a = (className *)pa; \ + } \ + template \ + PUP::able *className::call_PUP_constructor(void) { \ + className *pupobj = new className((CkMigrateMessage *)0); \ + _MEMCHECK(pupobj); \ + return pupobj; \ + } \ + template \ + const PUP::able::PUP_ID &className::get_PUP_ID(void) const { \ + return className::my_PUP_ID; \ + } \ + template \ + void className::register_PUP_ID(const char *name) { \ + my_PUP_ID = register_constructor(name, className::call_PUP_constructor); \ + } + +// Declarations to include in an abstract PUP::able's body. +// Abstract PUP::ables do not need def or reg. +#define PUPable_abstract(className) \ +public: \ + virtual const PUP::able::PUP_ID &get_PUP_ID(void) const = 0; \ + PUPable_operator_inside(className) + +// Definitions to include exactly once at file scope +#define PUPable_def(className) \ + PUP::able *className::call_PUP_constructor(void) { \ + className *pupobj = new className((CkMigrateMessage *)0); \ + _MEMCHECK(pupobj); \ + return pupobj; \ + } \ + const PUP::able::PUP_ID &className::get_PUP_ID(void) const { \ + return className::my_PUP_ID; \ + } \ + PUP::able::PUP_ID className::my_PUP_ID; \ + void className::register_PUP_ID(const char *name) { \ + my_PUP_ID = register_constructor(name, className::call_PUP_constructor); \ + } + +// Definitions to include exactly once at file scope +#define PUPable_def_template(className) \ + template <> PUP::able::PUP_ID className::my_PUP_ID = 0; + +// Definitions to include exactly once at file scope +#define PUPable_def_template_nonInst(className) \ + PUP::able::PUP_ID className::my_PUP_ID = 0; + +// Code to execute exactly once at program start time +#define PUPable_reg(className) className::register_PUP_ID(#className); +#define PUPable_reg2(classIdentifier, className) \ + classIdentifier::register_PUP_ID(className); + +inline void operator|(er &p, able &a) { a.pup(p); } +inline void operator|(er &p, able *&a) { p(&a); } +} // namespace PUP + +// Holds a pointer to a (possibly dynamically allocated) PUP::able. +// Extracting the pointer hands the deletion responsibility over. +// This is used by parameter marshalling, which doesn't work well +// with bare pointers. +// CkPointer t is the parameter-marshalling equivalent of T *t +template class CkPointer { + T *allocated; // Pointer that PUP dynamically allocated for us (recv only) + T *ptr; // Read-only pointer + + CkPointer(const CkPointer &) = delete; + void operator=(const CkPointer &) = delete; + void operator=(CkPointer &&) = delete; + +protected: + T *peek(void) { return ptr; } + CkPointer(T *src, T *alloc) : allocated(alloc), ptr(src) {} + +public: + /// Used on the send side, and does *not* delete the object. + CkPointer(T *src) : CkPointer(src, nullptr) {} + /// Begin completely empty: used on marshalling recv side. + CkPointer(void) : CkPointer(nullptr, nullptr) {} + /// Move constructor (copy src fields, then invalidate) + CkPointer(CkPointer &&src) : CkPointer(src.ptr, src.allocated) { + src.allocated = src.ptr = nullptr; + } + + ~CkPointer() { + if (allocated) + delete allocated; + } + + /// Extract the object held by this class. + /// Deleting the pointer is now the user's responsibility + inline operator T *() { + allocated = 0; + return ptr; + } + + inline void pup(PUP::er &p) { + PUP::ptr_helper()(p, ptr); + if (p.isUnpacking()) { + allocated = ptr; + } + } + friend inline void operator|(PUP::er &p, CkPointer &v) { v.pup(p); } +}; +#define PUPable_marshall CkPointer + +// Like CkPointer, but keeps deletion responsibility forever. +// CkReference t is the parameter-marshalling equivalent of T &t +template class CkReference : private CkPointer { +public: + /// Used on the send side, and does *not* delete the object. + CkReference(T &src) ///< Marshall this object. + : CkPointer(&src) {} + + /// Begin completely empty: used on the recv side. + CkReference(void) {} + + /// Look at the object held by this class. Does *not* hand over + /// deletion responsiblity. + inline operator T &() { return *this->peek(); } + + inline void pup(PUP::er &p) { CkPointer::pup(p); } + + friend inline void operator|(PUP::er &p, CkReference &v) { v.pup(p); } +}; + +// For people that forget the "::" +typedef PUP::er PUPer; +typedef PUP::able PUPable; + +/******** PUP via pipe: another way to access PUP::ers ***** +The parameter marshalling system pups each variable v using just: + p|v; +Thus we need a "void operator|(PUP::er &p,T &v)" for all types +that work with parameter marshalling. +*/ + +namespace PUP { +/** + Traits class: decide if the type T can be safely + pupped as raw bytes. This is true of classes that + do not contain pointers and do not need pup routines. + Use this like: + if (PUP::as_bytes::value) { ... } +*/ +template class as_bytes { + /* default is to not pack as bytes by default */ +public: + enum { value = 0 }; +}; + +// Defines is_pupable to allow enums to be pupped in pup_stl.h +namespace details { + +template struct make_void { + typedef void type; +}; +template using void_t = typename make_void::type; + +template struct is_pupable : std::false_type {}; + +template +struct is_pupable< + T, void_t().pup(std::declval()))>> + : std::true_type {}; + +} // namespace details + +/** + Default operator|: call pup routine (as long as T has a pup function). +*/ +template ::value, int>::type = 0> +inline void operator|(PUP::er &p, T &t) { + p.syncComment(PUP::sync_begin_object); + t.pup(p); + p.syncComment(PUP::sync_end_object); +} + +/** + Default PUParray: pup each element. +*/ +template inline void PUParray(PUP::er &p, T *t, size_t n) { + p.syncComment(PUP::sync_begin_array); + for (size_t i = 0; i < n; i++) { + p.syncComment(PUP::sync_item); + p | t[i]; + } + p.syncComment(PUP::sync_end_array); +} + +/// Copy this type as raw memory (like memcpy). +#define PUPbytes(type) \ + namespace PUP { \ + inline void operator|(PUP::er &p, type &t) { p((char *)&t, sizeof(type)); } \ + } \ + namespace PUP { \ + inline void PUParray(PUP::er &p, type *ta, size_t n) { \ + p((char *)ta, n * sizeof(type)); \ + } \ + } \ + namespace PUP { \ + template <> class as_bytes { \ + public: \ + enum { value = 1 }; \ + }; \ + } + +/// Make PUP work with this function pointer type, copied as raw bytes. +#define PUPfunctionpointer(fnPtrType) \ + inline void operator|(PUP::er &p, fnPtrType &t) { \ + p((char *)&t, sizeof(fnPtrType)); \ + } + +/// Make PUP work with this enum type, copied as an "int". +#define PUPenum(enumType) \ + inline void operator|(PUP::er &p, enumType &e) { \ + int v = e; \ + p | v; \ + e = v; \ + } + +} // namespace PUP + +/** + For all builtin types, like "int", + operator| and PUParray use p(t) and p(ta,n). +*/ +#define PUP_BUILTIN_SUPPORT(type) \ + namespace PUP { \ + inline void operator|(er &p, type &t) { p(t); } \ + } \ + namespace PUP { \ + inline void PUParray(er &p, type *ta, size_t n) { p(ta, n); } \ + } \ + namespace PUP { \ + template <> class as_bytes { \ + public: \ + enum { value = 1 }; \ + }; \ + } +PUP_BUILTIN_SUPPORT(signed char) +#if CMK_SIGNEDCHAR_DIFF_CHAR +PUP_BUILTIN_SUPPORT(char) +#endif +PUP_BUILTIN_SUPPORT(unsigned char) +PUP_BUILTIN_SUPPORT(short) +PUP_BUILTIN_SUPPORT(int) +PUP_BUILTIN_SUPPORT(long) +PUP_BUILTIN_SUPPORT(unsigned short) +PUP_BUILTIN_SUPPORT(unsigned int) +PUP_BUILTIN_SUPPORT(unsigned long) +PUP_BUILTIN_SUPPORT(float) +PUP_BUILTIN_SUPPORT(double) +PUP_BUILTIN_SUPPORT(bool) +#if CMK_LONG_DOUBLE_DEFINED +PUP_BUILTIN_SUPPORT(long double) +#endif +#ifdef CMK_PUP_LONG_LONG +PUP_BUILTIN_SUPPORT(CMK_PUP_LONG_LONG) +PUP_BUILTIN_SUPPORT(unsigned CMK_PUP_LONG_LONG) +#endif +#if CMK_HAS_INT16 +PUP_BUILTIN_SUPPORT(CmiInt16) +PUP_BUILTIN_SUPPORT(CmiUInt16) +#endif + +// This macro is useful in simple pup routines: +// It's just p|x, but it also documents the *name* of the variable. +// You must have a PUP::er named p. +#define PUPn(field) \ + do { \ + if (p.hasComments()) \ + p.comment(#field); \ + p | field; \ + } while (0) + +// Like PUPn(x), above, but for arrays. +#define PUPv(field, len) \ + do { \ + if (p.hasComments()) \ + p.comment(#field); \ + PUParray(p, field, len); \ + } while (0) + +namespace PUP { +template struct ptr_helper { + inline void operator()(PUP::er &p, T *&t) const { + bool is_nullptr = nullptr == t; + p | is_nullptr; + if (!is_nullptr) { + PUP::able *t_able = static_cast(t); + p | t_able; + if (p.isUnpacking()) + t = static_cast(t_able); + } + } +}; + +template struct ptr_helper { + inline void operator()(PUP::er &p, T *&t) const { + bool is_nullptr = nullptr == t; + p | is_nullptr; + if (!is_nullptr) { + if (p.isUnpacking()) { + initialize_ptr(t); + } + p | *t; + } + } + +protected: + template + inline typename std::enable_if< + std::is_constructible::value>::type + initialize_ptr(U *&u) const { + u = new U(reconstruct()); + } + + template + inline typename std::enable_if< + !std::is_constructible::value>::type + initialize_ptr(U *&u) const { + u = new U(); + } +}; +} // namespace PUP + +#endif // def __CK_PUP_H diff --git a/src/pup/pup_c.C b/src/pup/pup_c.C new file mode 100644 index 0000000..8e2bb3c --- /dev/null +++ b/src/pup/pup_c.C @@ -0,0 +1,204 @@ +/* +Pack/UnPack Library for UIUC Parallel Programming Lab +Orion Sky Lawlor, olawlor@uiuc.edu, 9/11/2000 + +This file maps the C bindings for the PUP library onto +the C++ implementation. This file is C++, but all its +routines are C-callable. + +I realize it looks horrible, but it's just *interface* code-- +there's nothing actually happening here. +*/ +#include "pup.h" +#include "pup_c.h" +#include "pup_toNetwork.h" +#include "charm-api.h" + + +/*This maps the opaque C "pup_er p" type to +a C++ "PUP::er &" type. We actually want a + "*reinterpret_cast(p)" +*/ +#define mp (*(PUP::er *)p) + +/*Allocate PUP::er of different kind */ +CLINKAGE pup_er pup_new_sizer(void) + { return new PUP::sizer; } +CLINKAGE pup_er pup_new_toMem(void *Nbuf) + { return new PUP::toMem(Nbuf); } +CLINKAGE pup_er pup_new_fromMem(const void *Nbuf) + { return new PUP::fromMem(Nbuf); } +CLINKAGE pup_er pup_new_network_sizer(void) + { return new PUP_toNetwork_sizer; } +CLINKAGE pup_er pup_new_network_pack(void *Nbuf) + { return new PUP_toNetwork_pack(Nbuf); } +CLINKAGE pup_er pup_new_network_unpack(const void *Nbuf) + { return new PUP_toNetwork_unpack(Nbuf); } +#if CMK_CCS_AVAILABLE +#include "ccs-builtins.h" +CLINKAGE pup_er pup_new_fmt(pup_er p) + { return new PUP_fmt(mp); } +CLINKAGE void pup_fmt_sync_begin_object(pup_er p) + { mp.synchronize(PUP::sync_begin_object); } +CLINKAGE void pup_fmt_sync_end_object(pup_er p) + { mp.synchronize(PUP::sync_end_object); } +CLINKAGE void pup_fmt_sync_begin_array(pup_er p) + { mp.synchronize(PUP::sync_begin_array); } +CLINKAGE void pup_fmt_sync_end_array(pup_er p) + { mp.synchronize(PUP::sync_end_array); } +CLINKAGE void pup_fmt_sync_item(pup_er p) + { mp.syncComment(PUP::sync_item); } +#endif +CLINKAGE void pup_destroy(pup_er p) + { delete ((PUP::er *)p); } + +/*Determine what kind of pup_er we have-- +return 1 for true, 0 for false.*/ +CLINKAGE int pup_isPacking(const pup_er p) + { return (mp.isPacking())?1:0;} +CLINKAGE int pup_isUnpacking(const pup_er p) + { return (mp.isUnpacking())?1:0;} +CLINKAGE int pup_isSizing(const pup_er p) + { return (mp.isSizing())?1:0;} +CLINKAGE int pup_isDeleting(const pup_er p) + { return (mp.isDeleting())?1:0;} +CLINKAGE int pup_isUserlevel(const pup_er p) + { return (mp.isUserlevel())?1:0;} +CLINKAGE int pup_isCheckpoint(const pup_er p) + { return (mp.isCheckpoint())?1:0;} +CLINKAGE int pup_isMigration(const pup_er p) + { return (mp.isMigration())?1:0;} +CLINKAGE char* pup_typeString(const pup_er p) + { return (char *)mp.typeString(); } + +FLINKAGE int FTN_NAME(FPUP_ISPACKING,fpup_ispacking)(const pup_er p) + { return (mp.isPacking())?1:0;} +FLINKAGE int FTN_NAME(FPUP_ISUNPACKING,fpup_isunpacking)(const pup_er p) + { return (mp.isUnpacking())?1:0;} +FLINKAGE int FTN_NAME(FPUP_ISSIZING,fpup_issizing)(const pup_er p) + { return (mp.isSizing())?1:0;} +FLINKAGE int FTN_NAME(FPUP_ISDELETING,fpup_isdeleting)(const pup_er p) + { return (mp.isDeleting())?1:0;} +FLINKAGE int FTN_NAME(FPUP_ISUSERLEVEL,fpup_isuserlevel)(const pup_er p) + { return (mp.isUserlevel())?1:0;} + +/*Read the size of the pupper */ +CLINKAGE size_t pup_size(const pup_er p) + { return mp.size(); } + +#define SIZE_APPROX_BITS 13 + +/* Utilities to approximately encode large sizes, within 0.5% */ +CLINKAGE CMK_TYPEDEF_UINT2 pup_encodeSize(size_t s) +{ + // Use the top two bits to indicate a scaling factor as a power of 256. At + // each step up in size, we'll thus lose the bottom 8 bits out of 14: + // 256/32k < 1% + CmiUInt2 power = 0; + + while (s > (1UL << SIZE_APPROX_BITS) - 1) { + power++; + if (s & (1UL << 6)) // Round up as needed to cut relative error in half + s += (1UL << 7); + s >>= 8; + } + + return (power << SIZE_APPROX_BITS) | s; +} + +CLINKAGE size_t pup_decodeSize(CMK_TYPEDEF_UINT2 a) +{ + CmiUInt2 power = a >> SIZE_APPROX_BITS; + size_t factor = 1UL << (8 * power); + + size_t base = a & ((1UL << SIZE_APPROX_BITS) - 1); + + return base * factor; +} + +/*Insert a synchronization into the data stream */ +CLINKAGE void pup_syncComment(const pup_er p, unsigned int sync, const char *message) + { mp.syncComment(sync, message); } +/*FLINKAGE void FNT_NAME(FPUP_SYNCCOMMENT,fpup_syncComment)(const pup_er p, unsigned int sync, const char *message) + { mp.syncComment(sync, message); }*/ +CLINKAGE void pup_comment(const pup_er p, const char *message) + { mp.comment(message); } + +#undef PUP_BASIC_DATATYPE /*from pup_c.h*/ +#undef PUP_BASIC_DATATYPEF /*from pup_c.h*/ + +/*Pack/unpack data items, declared with macros for brevity. +The macros expand like: +void pup_int(pup_er p,int *i) <- single integer pack/unpack + {(PUP::er & cast p)(*i);} +void pup_ints(pup_er p,int *iarr,size_t nItems) <- array pack/unpack + {(PUP::er * cast p)(iarr,nItems);} +*/ +#define PUP_BASIC_DATATYPE(typeName,type) \ + CLINKAGE void pup_##typeName(pup_er p,type *v) \ + {mp(*v);} \ + CLINKAGE void pup_##typeName##s(pup_er p,type *arr,size_t nItems) \ + {mp(arr,nItems);} + +PUP_BASIC_DATATYPE(char,char) +PUP_BASIC_DATATYPE(short,short) +PUP_BASIC_DATATYPE(int,int) +PUP_BASIC_DATATYPE(long,long) +PUP_BASIC_DATATYPE(uchar,unsigned char) +PUP_BASIC_DATATYPE(ushort,unsigned short) +PUP_BASIC_DATATYPE(uint,unsigned int) +PUP_BASIC_DATATYPE(ulong,unsigned long) +PUP_BASIC_DATATYPE(float,float) +PUP_BASIC_DATATYPE(double,double) +PUP_BASIC_DATATYPE(int8,CMK_TYPEDEF_INT8) +PUP_BASIC_DATATYPE(size_t,size_t) + +// Pointers have a different signature, so they need special treatment +CLINKAGE void pup_pointer(pup_er p,void **v) {mp(*v,(void*)NULL);} +CLINKAGE void pup_pointers(pup_er p,void **arr,size_t nItems) {mp(arr,nItems,(void*)NULL);} + +#define PUP_BASIC_DATATYPEF(typeUP,typelo,type) \ + FLINKAGE void FTN_NAME(FPUP_##typeUP,fpup_##typelo)(pup_er p,type *v) \ + {mp(*v);} \ + FLINKAGE void FTN_NAME(FPUP_##typeUP##SG,fpup_##typelo##sg)(pup_er p,type *arr,int *nItems) \ + {mp(arr,*nItems);} \ + FLINKAGE void FTN_NAME(FPUP_##typeUP##S,fpup_##typelo##s)(pup_er p,type *arr,int *nItems) \ + {mp(arr,*nItems);} + +PUP_BASIC_DATATYPEF(CHAR,char,char) +PUP_BASIC_DATATYPEF(SHORT,short,short) +PUP_BASIC_DATATYPEF(INT,int,int) +PUP_BASIC_DATATYPEF(LONG,long,long) +PUP_BASIC_DATATYPEF(REAL,real,float) +PUP_BASIC_DATATYPEF(DOUBLE,double,double) +PUP_BASIC_DATATYPEF(LOGICAL,logical,int) + + +FLINKAGE void FTN_NAME(FPUP_COMPLEX,fpup_complex)(pup_er p, float *v) +{mp(v,2);} + +FLINKAGE void FTN_NAME(FPUP_COMPLEXESG,fpup_complexesg)(pup_er p, float *arr, int *nItems) +{mp(arr,2*(*nItems));} + +FLINKAGE void FTN_NAME(FPUP_COMPLEXES,fpup_complexes)(pup_er p, float *arr, int *nItems) +{mp(arr,2*(*nItems));} + +FLINKAGE void FTN_NAME(FPUP_DOUBLECOMPLEX,fpup_doublecomplex)(pup_er p, double *v) +{mp(v,2);} + +FLINKAGE void FTN_NAME(FPUP_DOUBLECOMPLEXESG,fpup_doublecomplexesg)(pup_er p, double *arr, int *nItems) +{mp(arr,2*(*nItems));} + +FLINKAGE void FTN_NAME(FPUP_DOUBLECOMPLEXES,fpup_doublecomplexes)(pup_er p, double *arr, int *nItems) +{mp(arr,2*(*nItems));} + +/*Pack/unpack untyped byte array:*/ +CLINKAGE void pup_bytes(pup_er p,void *ptr,size_t nBytes) +{ + mp((char *)ptr,nBytes); +} + +FLINKAGE void FTN_NAME(FPUP_BYTES,fpup_bytes)(pup_er p,void *ptr,int *nBytes) +{ + mp((char *)ptr,*nBytes); +} diff --git a/src/pup/pup_c.h b/src/pup/pup_c.h new file mode 100644 index 0000000..5a3e454 --- /dev/null +++ b/src/pup/pup_c.h @@ -0,0 +1,118 @@ +#ifndef _PUP_C_H +#define _PUP_C_H + +/* +Pack/UnPack Library for UIUC Parallel Programming Lab +C Bindings version +Orion Sky Lawlor, olawlor@uiuc.edu, 9/11/2000 + +This library allows you to easily pack an array, structure, +or object into a memory buffer or disk file, and then read +the object back later. The library will also handle translating +between different machine representations for integers and floats. + +This is the C binding-- the main library is in C++. +From C, you can write a pup routine (the client part of the +library), but you can't make pup_er's (the system part). + +Typically, the user has to write separate functions for buffer +sizing, pack to memory, unpack from memory, pack to disk, and +unpack from disk. These functions all perform the exact same function-- +namely, they list the members of the array, struct, or object. +Further, all the functions must agree, or the unpacked data will +be garbage. This library allows the user to write *one* function, +which will perform all needed packing/unpacking. + +A simple example is: +typedef struct foo { + int x; + char y; + unsigned long z; + float q[3]; +} foo; + +void pup_foo(pup_er p,foo *f) +{ + pup_int(p,&f->x); + pup_char(p,&f->y); + pup_ulong(p,&f->z); + pup_floats(p,f->q,3); +} + +A more complex example is: +typedef struct bar { + foo f; + int nArr; <- Length of array below + double *arr; <- Heap-allocated array +} bar; + +void pup_bar(pup_er p,bar *b) +{ + pup_foo(p,&b->f); + pup_int(p,&b->nArr); + if (pup_isUnpacking(p)) + b->arr=(double *)malloc(b->nArr*sizeof(double)); + pup_doubles(p,b->arr,b->nArr); +} + +*/ + +#include "converse_config.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*This is actually a PUP::er *, cast to void *. + From C++, you can pass "&p" as a pup_er. +*/ +typedef void *pup_er; + +#ifndef AMPI_INTERNAL_SKIP_FUNCTIONS + +#define AMPI_CUSTOM_FUNC(return_type, function_name, ...) \ + extern return_type function_name(__VA_ARGS__); + +#include "pup_c_functions.h" + +#undef AMPI_CUSTOM_FUNC + +#endif /* !defined AMPI_INTERNAL_SKIP_FUNCTIONS */ + +/* These MUST match the sync declarations in pup.h */ +enum { + pup_sync_builtin = 0x70000000, /* Built-in, standard sync codes begin here */ + pup_sync_begin = + pup_sync_builtin + 0x01000000, /* Sync code at start of collection */ + pup_sync_end = + pup_sync_builtin + 0x02000000, /* Sync code at end of collection */ + pup_sync_last_system = + pup_sync_builtin + + 0x09000000, /* Sync code at end of "system" portion of object */ + pup_sync_array_m = + 0x00100000, /* Linear-indexed (0..n) array-- use item to separate */ + pup_sync_list_m = 0x00200000, /* Some other collection-- use index and item */ + pup_sync_object_m = 0x00300000, /* Sync mask for general object */ + + pup_sync_begin_array = pup_sync_begin + pup_sync_array_m, + pup_sync_begin_list = pup_sync_begin + pup_sync_list_m, + pup_sync_begin_object = pup_sync_begin + pup_sync_object_m, + + pup_sync_end_array = pup_sync_end + pup_sync_array_m, + pup_sync_end_list = pup_sync_end + pup_sync_list_m, + pup_sync_end_object = pup_sync_end + pup_sync_object_m, + + pup_sync_item = + pup_sync_builtin + 0x00110000, /* Sync code for a list or array item */ + pup_sync_index = + pup_sync_builtin + 0x00120000, /* Sync code for index of item in a list */ + + pup_sync_last +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/pup/pup_c_functions.h b/src/pup/pup_c_functions.h new file mode 100644 index 0000000..9ffa386 --- /dev/null +++ b/src/pup/pup_c_functions.h @@ -0,0 +1,78 @@ +/* + * This file is separate from pup_c.h because PUP is included as part + * of AMPI's extensions to the MPI standard, and certain global variable + * privatization methods require the AMPI API to be exposed as function pointers + * through a shim and loader mechanism that needs to list the entire set of + * provided functions at multiple points in its implementation. + * + * See src/libs/ck-libs/ampi/ampi_functions.h for mandatory procedures. + * + * For ease of reading: AMPI_CUSTOM_FUNC(ReturnType, FunctionName, Parameters...) + */ + +/*Allocate PUP::er of different kind */ +AMPI_CUSTOM_FUNC(pup_er, pup_new_sizer, void) +AMPI_CUSTOM_FUNC(pup_er, pup_new_toMem, void *Nbuf) +AMPI_CUSTOM_FUNC(pup_er, pup_new_fromMem, const void *Nbuf) +AMPI_CUSTOM_FUNC(pup_er, pup_new_network_sizer, void) +AMPI_CUSTOM_FUNC(pup_er, pup_new_network_pack, void *Nbuf) +AMPI_CUSTOM_FUNC(pup_er, pup_new_network_unpack, const void *Nbuf) +#if CMK_CCS_AVAILABLE +AMPI_CUSTOM_FUNC(pup_er, pup_new_fmt, pup_er p) +AMPI_CUSTOM_FUNC(void, pup_fmt_sync_begin_object, pup_er p) +AMPI_CUSTOM_FUNC(void, pup_fmt_sync_end_object, pup_er p) +AMPI_CUSTOM_FUNC(void, pup_fmt_sync_begin_array, pup_er p) +AMPI_CUSTOM_FUNC(void, pup_fmt_sync_end_array, pup_er p) +AMPI_CUSTOM_FUNC(void, pup_fmt_sync_item, pup_er p) +#endif +AMPI_CUSTOM_FUNC(void, pup_destroy, pup_er p) + +/*Determine what kind of pup_er we have-- +return 1 for true, 0 for false.*/ +AMPI_CUSTOM_FUNC(int, pup_isPacking, const pup_er p) +AMPI_CUSTOM_FUNC(int, pup_isUnpacking, const pup_er p) +AMPI_CUSTOM_FUNC(int, pup_isSizing, const pup_er p) +AMPI_CUSTOM_FUNC(int, pup_isDeleting, const pup_er p) +AMPI_CUSTOM_FUNC(int, pup_isUserlevel, const pup_er p) +AMPI_CUSTOM_FUNC(int, pup_isCheckpoint, const pup_er p) +AMPI_CUSTOM_FUNC(int, pup_isMigration, const pup_er p) +AMPI_CUSTOM_FUNC(char *, pup_typeString, const pup_er p) + +/*Insert a synchronization into the data stream */ +AMPI_CUSTOM_FUNC(void, pup_syncComment, const pup_er p, unsigned int sync, const char *message) +AMPI_CUSTOM_FUNC(void, pup_comment, const pup_er p, const char *message) + +/*Read the size of a pupper */ +AMPI_CUSTOM_FUNC(size_t, pup_size, const pup_er p) + +/* Utilities to approximately encode large sizes, within 0.5% */ +AMPI_CUSTOM_FUNC(CMK_TYPEDEF_UINT2, pup_encodeSize, size_t s) +AMPI_CUSTOM_FUNC(size_t, pup_decodeSize, CMK_TYPEDEF_UINT2 a) + +/*Pack/unpack data items, declared with macros for brevity. +The macros expand like: +AMPI_CUSTOM_FUNC(void, pup_int, pup_er p,int *i) // <- single integer pack/unpack +AMPI_CUSTOM_FUNC(void, pup_ints, pup_er p,int *iarr,int nItems) // <- array pack/unpack +*/ +#define PUP_BASIC_DATATYPE(typeName,type) \ + AMPI_CUSTOM_FUNC(void, pup_##typeName, pup_er p,type *v) \ + AMPI_CUSTOM_FUNC(void, pup_##typeName##s, pup_er p,type *arr,size_t nItems) + +PUP_BASIC_DATATYPE(char,char) +PUP_BASIC_DATATYPE(short,short) +PUP_BASIC_DATATYPE(int,int) +PUP_BASIC_DATATYPE(long,long) +PUP_BASIC_DATATYPE(uchar,unsigned char) +PUP_BASIC_DATATYPE(ushort,unsigned short) +PUP_BASIC_DATATYPE(uint,unsigned int) +PUP_BASIC_DATATYPE(ulong,unsigned long) +PUP_BASIC_DATATYPE(float,float) +PUP_BASIC_DATATYPE(double,double) +PUP_BASIC_DATATYPE(pointer,void*) +PUP_BASIC_DATATYPE(int8, CMK_TYPEDEF_INT8) +PUP_BASIC_DATATYPE(size_t, size_t) + +#undef PUP_BASIC_DATATYPE + +/*Pack/unpack untyped byte array:*/ +AMPI_CUSTOM_FUNC(void, pup_bytes, pup_er p,void *ptr,size_t nBytes) diff --git a/src/pup/pup_cmialloc.C b/src/pup/pup_cmialloc.C new file mode 100644 index 0000000..58891f4 --- /dev/null +++ b/src/pup/pup_cmialloc.C @@ -0,0 +1,88 @@ +#include "pup_cmialloc.h" + +void PUP_cmiAllocSizer::bytes(void *,size_t n,size_t itemSize,PUP::dataType) { + nBytes += n * itemSize; +} + +void PUP_toCmiAllocMem::bytes(void *p, size_t n, size_t itemSize, + PUP::dataType t) { + + n *= itemSize; + memcpy((void *)buf, p, n); + buf += n; +} + +void PUP_fromCmiAllocMem::bytes(void *p, size_t n, size_t itemSize, + PUP::dataType t) +{ + n*=itemSize; + memcpy(p,(const void *)buf,n); + + buf+= n; +} + +void PUP_cmiAllocSizer::pupCmiAllocBuf(void **msg) { + CmiChunkHeader chnk_hdr = *(BLKSTART(*msg)); + pupCmiAllocBuf(msg, chnk_hdr.size); +} + +void PUP_cmiAllocSizer::pupCmiAllocBuf(void **msg, size_t msg_size) { + + //The cmialloced buf can only start at an aligned memory location + //So nbytes has to be aligned + nBytes = ALIGN8(nBytes); + + nBytes += sizeof(CmiChunkHeader); + //Here the user buffer pointer will start, hence everything has to + //be aligned till here + nBytes += msg_size; //The actual size of the user message +} + + +void PUP_toCmiAllocMem::pupCmiAllocBuf(void **msg) { + pupCmiAllocBuf(msg, SIZEFIELD(msg)); +} + +void PUP_toCmiAllocMem::pupCmiAllocBuf(void **msg, size_t msg_size) { + + CmiChunkHeader chnk_hdr; + + buf = origBuf + ALIGN8_LONG(size()); + + chnk_hdr.size = msg_size; + chnk_hdr.setRef(origBuf - (buf + sizeof(CmiChunkHeader))); + + //Copy the Chunk header + memcpy(buf, &chnk_hdr, sizeof(CmiChunkHeader)); + buf += sizeof(CmiChunkHeader); + + //Now buf is a memory aligned pointer + //Copy the message + //While unpacking, this aligned pointer will be returned + memcpy(buf, *msg, msg_size); + buf += msg_size; +} + +void PUP_fromCmiAllocMem::pupCmiAllocBuf(void **msg) { + //First align buf + buf = (PUP::myByte *)(intptr_t)ALIGN8_LONG((intptr_t)buf); + + //Now get the chunk header + CmiChunkHeader chnk_hdr; + //Get the Chunk header + memcpy(&chnk_hdr, buf, sizeof(CmiChunkHeader)); + buf += sizeof(CmiChunkHeader); + + //Now we are at the begining of the user buffer + *msg = buf; + + //Move the local buf forward by size bytes + buf += chnk_hdr.size; + + //update the reference count of the original buf + REFFIELDINC(origBuf); +} + + + +/***** END CmiAlloc'ed buffer management functions ***********/ diff --git a/src/pup/pup_cmialloc.h b/src/pup/pup_cmialloc.h new file mode 100644 index 0000000..46c2a9b --- /dev/null +++ b/src/pup/pup_cmialloc.h @@ -0,0 +1,144 @@ + +#ifndef PUP_CMIALLOC_H__ +#define PUP_CMIALLOC_H__ + +#include "pup.h" +#include "converse.h" + +#include +#include +#include +#include + +/******** CMIALLOC buffer management functions ******/ + +/* Given a user chunk m, extract the enclosing chunk header fields: */ + +//Align data sizes to 8 bytes +#define ALIGN8_LONG(x) (intptr_t)((~7)&((x)+7)) + +//Assuming Size of CmiChunkHeader is a multiple of 8 bytes!! + +/*For CMI alloc'ed memory + CmiAlloc currently has the following memory footprint + + |int size|int ref|user allocated buffer| + + The ref count can be set so that a sub buffers can be individually + freed. When all sub buffers have been individually freed the + super buffer is freed. This is very use ful for message combining. + + For example, the memory foot print of a combined message could + look like this + + |size_bigbuf|ref_bigbuf|begin_bigbuf + ..... |size_subbuf1|ref_subbuf1|subbuf1| + .... |size_subbuf2|ref_subbuf2|subbuf2| + ..... |size_subbufk|ref_subbufk|subbufk| + + The usr program can then get use each of the sub_bufs + sub_buf1, ... , sub_bufk + + These sub_bufs could be converse messages or just data pointers + + To create such a combined message, the pup framwork can be used + + PUP_cmiAllocSizer psizer; + for(count = 0; count < k; count++) + psizer.pupCmiAllocBuf(&sub_buf[k]); + + void *bigbuf = CmiAlloc(psizer.size()); + PUP_toCmiAllocMem pmem(bigbuf); + + for(count = 0; count < k; count++) + pmem.pupCmiAllocBuf(&sub_buf[k]); + + //NOW big buf has all the subbuffers + //If you also add a converse header to it it can become a + //converse message + + //To extract sub buffers from bigbuf + PUP_fromCmiAllocMem pfmem(bigbuf); + + //Notice not memory allocation or copying needed on the + //destination + for(count = 0; count < k; count++) + pfmem.pupCmiAllocBuf(&sub_buf[k]); + + + To free these buffers the user program on the destination after + receiving the messages MUST CALL!! + + CmiFree(sub_buf[1]); + .... + CmiFree(sub_buf[k]); + */ + +/** + The current test for this code is in src/ck-com/MsgPacker.C + I will later port CmiMultipleSend +**/ + +class PUP_cmiAllocSizer : public PUP::sizer { + protected: + //Generic bottleneck: n items of size itemSize + virtual void bytes(void *p,size_t n,size_t itemSize,PUP::dataType t); + public: + //Write data to the given buffer + PUP_cmiAllocSizer(void): PUP::sizer() {} + + //Must be a CmiAlloced buf while packing + void pupCmiAllocBuf(void **msg); + + //In case source is not CmiAlloced the size can be passed and any + //user buf can be converted into a cmialloc'ed buf + void pupCmiAllocBuf(void **msg, size_t msg_size); +}; + + +//For packing into a preallocated, presized memory cmialloc'ed buffer +//Can use reference counting to reduce one level of copying on the +//receiver +class PUP_toCmiAllocMem : public PUP::toMem { + protected: + //Generic bottleneck: pack n items of size itemSize from p. + virtual void bytes(void *p,size_t n,size_t itemSize, PUP::dataType t); + + public: + //Write data to the given buffer + PUP_toCmiAllocMem(size_t size): PUP::toMem(CmiAlloc(size)) {} + PUP_toCmiAllocMem(void *buf): PUP::toMem(buf) {} + + //Copy the size of the buffer and the reference count while packing + //Get the buffer directly from the message while unpacking + //Saves on a copy + void pupCmiAllocBuf(void **msg); + + //Non cmialloc'ed buffers can also be passed and pupped as a + //cmialloc'ed buffers + void pupCmiAllocBuf(void **msg, size_t size); +}; + + +//For unpacking from a memory buffer +class PUP_fromCmiAllocMem : public PUP::fromMem { + protected: + //Generic bottleneck: unpack n items of size itemSize from p. + virtual void bytes(void *p,size_t n,size_t itemSize, PUP::dataType t); + public: + //Read data from the given buffer + //The buffer SHOULD have been CMIALLOC'ed + PUP_fromCmiAllocMem(const void *Nbuf): PUP::fromMem(Nbuf) {} + + //Copy the size of the buffer and the reference count while packing + //Get the buffer directly from the message while unpacking + //Saves on a copy + void pupCmiAllocBuf(void **msg); + + //size is irrelevant and for consistency with toCmiAllocMem + void pupCmiAllocBuf(void **msg, size_t size) { + pupCmiAllocBuf(msg); + } +}; + +#endif diff --git a/src/pup/pup_f.f90.sh b/src/pup/pup_f.f90.sh new file mode 100755 index 0000000..fb1b576 --- /dev/null +++ b/src/pup/pup_f.f90.sh @@ -0,0 +1,501 @@ +#!/bin/bash +# +# Shell script to create the pup_f.f90 file. +# Used to avoid duplicate copy-and-paste code in pup_f.f90. + +cat > pup_f.f90 << END_OF_HEADER +! DO NOT EDIT THIS FILE, GENERATE IT FROM RUNNING pup_f.f90.sh + module pupmod + implicit none + + interface + function fpup_issizing(p) + INTEGER :: p + logical fpup_issizing + end function + function fpup_ispacking(p) + INTEGER :: p + logical fpup_ispacking + end function + function fpup_isunpacking(p) + INTEGER :: p + logical fpup_isunpacking + end function + function fpup_isdeleting(p) + INTEGER :: p + logical fpup_isdeleting + end function + function fpup_isuserlevel(p) + INTEGER :: p + logical fpup_isuserlevel + end function + + subroutine fpup_char(p, d) + INTEGER :: p + CHARACTER :: d + end subroutine + subroutine fpup_short(p, d) + INTEGER :: p + INTEGER (KIND=2) :: d + end subroutine + subroutine fpup_int(p, d) + INTEGER :: p + INTEGER (KIND=4) :: d + end subroutine + subroutine fpup_long(p, d) + INTEGER :: p + INTEGER (KIND=8) :: d + end subroutine + subroutine fpup_real(p, d) + INTEGER :: p + REAL (KIND=4) :: d + end subroutine + subroutine fpup_double(p, d) + INTEGER :: p + REAL (KIND=8) :: d + end subroutine + subroutine fpup_logical(p, d) + INTEGER :: p + LOGICAL :: d + end subroutine + + subroutine fpup_complex(p, d) + INTEGER :: p + COMPLEX*8 :: d + end subroutine + + subroutine fpup_doublecomplex(p, d) + INTEGER :: p + COMPLEX*16 :: d + end subroutine + + end interface + +END_OF_HEADER + +for t in chars ints longs reals doubles logicals complexes doublecomplexes +do + echo " interface fpup_${t}" >> pup_f.f90 + if test $t = "chars" + then + echo " module procedure fpup_${t}_0" >> pup_f.f90 + fi + for i in 1 2 3 4 5 6 7 + do + echo " module procedure fpup_${t}_${i}" >> pup_f.f90 + done + echo " end interface fpup_${t}" >> pup_f.f90 + echo >> pup_f.f90 +done + +cat >> pup_f.f90 << END_OF_HEADER + interface pup + module procedure pi,pia1d,pia2d,pia3d,pia4d,pia5d,pia6d,pia7d + module procedure pc,pca1d,pca2d,pca3d,pca4d,pca5d,pca6d,pca7d + module procedure ps,psa1d,psa2d,psa3d,psa4d,psa5d,psa6d,psa7d + module procedure pr,pra1d,pra2d,pra3d,pra4d,pra5d,pra6d,pra7d + module procedure pd,pda1d,pda2d,pda3d,pda4d,pda5d,pda6d,pda7d + module procedure pl,pla1d,pla2d,pla3d,pla4d,pla5d,pla6d,pla7d + module procedure px,pxa1d,pxa2d,pxa3d,pxa4d,pxa5d,pxa6d,pxa7d + module procedure py,pya1d,pya2d,pya3d,pya4d,pya5d,pya6d,pya7d + end interface + interface apup + module procedure apia1d,apia2d,apia3d,apia4d,apia5d,apia6d,apia7d + module procedure apca1d,apca2d,apca3d,apca4d,apca5d,apca6d,apca7d + module procedure apsa1d,apsa2d,apsa3d,apsa4d,apsa5d,apsa6d,apsa7d + module procedure apra1d,apra2d,apra3d,apra4d,apra5d,apra6d,apra7d + module procedure apda1d,apda2d,apda3d,apda4d,apda5d,apda6d,apda7d + module procedure apla1d,apla2d,apla3d,apla4d,apla5d,apla6d,apla7d + module procedure apxa1d,apxa2d,apxa3d,apxa4d,apxa5d,apxa6d,apxa7d + module procedure apya1d,apya2d,apya3d,apya4d,apya5d,apya6d,apya7d + + ! NOTE: for compilers with full Fortran2003 support (GNU-4.9+, IC-15.0+, etc.) + ! ... we can provide a single apup interface for both pointers and allocatables + ! ... by simply removing the next two lines. + end interface + interface apup_al + module procedure apia1d_al,apia2d_al,apia3d_al,apia4d_al,apia5d_al,apia6d_al,apia7d_al + module procedure apca1d_al,apca2d_al,apca3d_al,apca4d_al,apca5d_al,apca6d_al,apca7d_al + module procedure apsa1d_al,apsa2d_al,apsa3d_al,apsa4d_al,apsa5d_al,apsa6d_al,apsa7d_al + module procedure apra1d_al,apra2d_al,apra3d_al,apra4d_al,apra5d_al,apra6d_al,apra7d_al + module procedure apda1d_al,apda2d_al,apda3d_al,apda4d_al,apda5d_al,apda6d_al,apda7d_al + module procedure apla1d_al,apla2d_al,apla3d_al,apla4d_al,apla5d_al,apla6d_al,apla7d_al + module procedure apxa1d_al,apxa2d_al,apxa3d_al,apxa4d_al,apxa5d_al,apxa6d_al,apxa7d_al + module procedure apya1d_al,apya2d_al,apya3d_al,apya4d_al,apya5d_al,apya6d_al,apya7d_al + end interface + contains + function pup_issz(p) + INTEGER :: p + logical pup_issz + pup_issz = fpup_issizing(p) + end function + function pup_ispk(p) + INTEGER :: p + logical pup_ispk + pup_ispk = fpup_ispacking(p) + end function + function pup_isupk(p) + INTEGER :: p + logical pup_isupk + pup_isupk = fpup_isunpacking(p) + end function + function pup_isdel(p) + INTEGER :: p + logical pup_isdel + pup_isdel = fpup_isdeleting(p) + end function + function pup_isul(p) + INTEGER :: p + logical pup_isul + pup_isul = fpup_isuserlevel(p) + end function + + + subroutine fpup_chars_0(p, d, c) + INTEGER :: p + CHARACTER(LEN=*) d + INTEGER :: c + call fpup_charsg(p, d, c) + end subroutine +END_OF_HEADER + +for data in "chars/character" "shorts/integer(kind=2)" "ints/integer(kind=4)" "longs/integer(kind=8)" "reals/real(kind=4)" "doubles/real(kind=8)" "logicals/logical"\ + "complexes/complex*8" "doublecomplexes/complex*16" +do + pupname=`echo $data | awk -F/ '{print $1}'` + typename=`echo $data | awk -F/ '{print $2}'` + for i in 1 2 3 4 5 6 7 + do + echo " subroutine fpup_${pupname}_${i}(p, d, c)" >> pup_f.f90 + echo " INTEGER :: p" >> pup_f.f90 + echo -n " ${typename}, intent(inout), dimension(:" >> pup_f.f90 + n=1 + while [ $n -lt $i ] + do + echo -n ",:" >> pup_f.f90 + n=`expr $n + 1` + done + echo ") :: d" >> pup_f.f90 + echo " INTEGER :: c" >> pup_f.f90 + echo " call fpup_${pupname}g(p, d, c)" >> pup_f.f90 + echo " end subroutine" >> pup_f.f90 + done + echo >> pup_f.f90 +done + +# +# Create pup routines for each data type: +# The "p" routines just copy the data. +# The "ap" routines also allocate and free the buffer. +# suffix _al means input is allocatable, otherwise its pointer +# +for data in "int/ints/i/integer" "short/shorts/s/integer(kind=2)" "char/chars/c/character" "real/reals/r/real(kind=4)" "double/doubles/d/real(kind=8)" "logical/logicals/l/logical"\ + "complex/complexes/x/complex*8" "doublecomplex/doublecomplexes/y/complex*16" +do + pupname=`echo $data | awk -F/ '{print $1}'` + pupnames=`echo $data | awk -F/ '{print $2}'` + cname=`echo $data | awk -F/ '{print $3}'` + fname=`echo $data | awk -F/ '{print $4}'` + echo "Making pup routines for data type $pupname/$cname/$fname" + cat >> pup_f.f90 << END_OF_DATATYPE + + + subroutine p${cname}(p, i) + INTEGER :: p + $fname, intent(inout) :: i + call fpup_${pupname}(p, i) + end subroutine + + subroutine p${cname}a1d(p, arr) + INTEGER :: p + $fname, intent(inout), dimension(:) :: arr + call fpup_${pupnames}(p, arr, size(arr)) + end subroutine + subroutine p${cname}a2d(p, arr) + INTEGER :: p + $fname, intent(inout), dimension(:,:) :: arr + call fpup_${pupnames}(p, arr, size(arr)) + end subroutine + subroutine p${cname}a3d(p, arr) + INTEGER :: p + $fname, intent(inout), dimension(:,:,:) :: arr + call fpup_${pupnames}(p, arr, size(arr)) + end subroutine + subroutine p${cname}a4d(p, arr) + INTEGER :: p + $fname, intent(inout), dimension(:,:,:,:) :: arr + call fpup_${pupnames}(p, arr, size(arr)) + end subroutine + subroutine p${cname}a5d(p, arr) + INTEGER :: p + $fname, intent(inout), dimension(:,:,:,:,:) :: arr + call fpup_${pupnames}(p, arr, size(arr)) + end subroutine + subroutine p${cname}a6d(p, arr) + INTEGER :: p + $fname, intent(inout), dimension(:,:,:,:,:,:) :: arr + call fpup_${pupnames}(p, arr, size(arr)) + end subroutine + subroutine p${cname}a7d(p, arr) + INTEGER :: p + $fname, intent(inout), dimension(:,:,:,:,:,:,:) :: arr + call fpup_${pupnames}(p, arr, size(arr)) + end subroutine + +END_OF_DATATYPE +done + +for arrkind in "pointer/associated/NULLIFY(arr)/" "allocatable/allocated/ /_al" +do + pointer=`echo $arrkind | awk -F/ '{print $1}'` + associated=`echo $arrkind | awk -F/ '{print $2}'` + NULLIFY=`echo $arrkind | awk -F/ '{print $3}'` + suffix=`echo $arrkind | awk -F/ '{print $4}'` + for data in "int/ints/i/integer" "short/shorts/s/integer(kind=2)" "char/chars/c/character" "real/reals/r/real(kind=4)" "double/doubles/d/real(kind=8)" "logical/logicals/l/logical"\ + "complex/complexes/x/complex*8" "doublecomplex/doublecomplexes/y/complex*16" + do + pupname=`echo $data | awk -F/ '{print $1}'` + pupnames=`echo $data | awk -F/ '{print $2}'` + cname=`echo $data | awk -F/ '{print $3}'` + fname=`echo $data | awk -F/ '{print $4}'` + echo "Making pup routines for data type $pupname/$cname/$fname" + cat >> pup_f.f90 << END_OF_DATATYPE + + subroutine ap${cname}a1d$suffix(p, arr) + INTEGER :: p + $fname, $pointer, dimension(:) :: arr + integer :: n(1) + IF (fpup_isunpacking(p)) THEN + CALL fpup_ints(p,n,1) + If (n(1) >= 0) THEN + ALLOCATE(arr(n(1))) + call fpup_${pupnames}(p, arr, n(1)) + ELSE + $NULLIFY + END If + ELSE ! packing + If ($associated(arr)) THEN + n(1)=SIZE(arr,DIM=1) + CALL fpup_ints(p,n,1) + call fpup_${pupnames}(p, arr, n(1)) + ELSE + n(1) = -1 + CALL fpup_ints(p,n,1) + End If + END IF + IF (fpup_isdeleting(p) .and. $associated(arr)) THEN + deallocate(arr) + END IF + end subroutine + + subroutine ap${cname}a2d$suffix(p, arr) + INTEGER :: p + $fname, $pointer, dimension(:,:) :: arr + integer :: n(2) + IF (fpup_isunpacking(p)) THEN + CALL fpup_ints(p,n,2) + If (n(1) >= 0) THEN + ALLOCATE(arr(n(1),n(2))) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + $NULLIFY + END If + ELSE ! packing + If ($associated(arr)) THEN + n(1)=SIZE(arr,DIM=1) + n(2)=SIZE(arr,DIM=2) + CALL fpup_ints(p,n,2) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + n(1) = -1 + n(2) = -1 + CALL fpup_ints(p,n,2) + End If + END IF + IF (fpup_isdeleting(p) .and. $associated(arr)) THEN + deallocate(arr) + END IF + end subroutine + + subroutine ap${cname}a3d$suffix(p, arr) + INTEGER :: p + $fname, $pointer, dimension(:,:,:) :: arr + integer :: n(3) + IF (fpup_isunpacking(p)) THEN + CALL fpup_ints(p,n,3) + If (n(1) >= 0) THEN + ALLOCATE(arr(n(1),n(2),n(3))) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + $NULLIFY + END If + ELSE ! packing + If ($associated(arr)) THEN + n(1)=SIZE(arr,DIM=1) + n(2)=SIZE(arr,DIM=2) + n(3)=SIZE(arr,DIM=3) + CALL fpup_ints(p,n,3) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + n(1) = -1 + n(2) = -1 + n(3) = -1 + CALL fpup_ints(p,n,3) + End If + END IF + IF (fpup_isdeleting(p) .and. $associated(arr)) THEN + deallocate(arr) + END IF + end subroutine + + subroutine ap${cname}a4d$suffix(p, arr) + INTEGER :: p + $fname, $pointer, dimension(:,:,:,:) :: arr + integer :: n(4) + IF (fpup_isunpacking(p)) THEN + CALL fpup_ints(p,n,4) + If (n(1) >= 0) THEN + ALLOCATE(arr(n(1),n(2),n(3),n(4))) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + $NULLIFY + END If + ELSE ! packing + If ($associated(arr)) THEN + n(1)=SIZE(arr,DIM=1) + n(2)=SIZE(arr,DIM=2) + n(3)=SIZE(arr,DIM=3) + n(4)=SIZE(arr,DIM=4) + CALL fpup_ints(p,n,4) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + n(1) = -1 + n(2) = -1 + n(3) = -1 + n(4) = -1 + CALL fpup_ints(p,n,4) + End If + END IF + IF (fpup_isdeleting(p) .and. $associated(arr)) THEN + deallocate(arr) + END IF + end subroutine + + subroutine ap${cname}a5d$suffix(p, arr) + INTEGER :: p + $fname, $pointer, dimension(:,:,:,:,:) :: arr + integer :: n(5) + IF (fpup_isunpacking(p)) THEN + CALL fpup_ints(p,n,5) + If (n(1) >= 0) THEN + ALLOCATE(arr(n(1),n(2),n(3),n(4),n(5))) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + $NULLIFY + END If + ELSE ! packing + If ($associated(arr)) THEN + n(1)=SIZE(arr,DIM=1) + n(2)=SIZE(arr,DIM=2) + n(3)=SIZE(arr,DIM=3) + n(4)=SIZE(arr,DIM=4) + n(5)=SIZE(arr,DIM=5) + CALL fpup_ints(p,n,5) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + n(1) = -1 + n(2) = -1 + n(3) = -1 + n(4) = -1 + n(5) = -1 + CALL fpup_ints(p,n,5) + End If + END IF + IF (fpup_isdeleting(p) .and. $associated(arr)) THEN + deallocate(arr) + END IF + end subroutine + + subroutine ap${cname}a6d$suffix(p, arr) + INTEGER :: p + $fname, $pointer, dimension(:,:,:,:,:,:) :: arr + integer :: n(6) + IF (fpup_isunpacking(p)) THEN + CALL fpup_ints(p,n,6) + If (n(1) >= 0) THEN + ALLOCATE(arr(n(1),n(2),n(3),n(4),n(5),n(6))) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + $NULLIFY + END If + ELSE ! packing + If ($associated(arr)) THEN + n(1)=SIZE(arr,DIM=1) + n(2)=SIZE(arr,DIM=2) + n(3)=SIZE(arr,DIM=3) + n(4)=SIZE(arr,DIM=4) + n(5)=SIZE(arr,DIM=5) + n(6)=SIZE(arr,DIM=6) + CALL fpup_ints(p,n,6) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + n(1) = -1 + n(2) = -1 + n(3) = -1 + n(4) = -1 + n(5) = -1 + n(6) = -1 + CALL fpup_ints(p,n,6) + End If + END IF + IF (fpup_isdeleting(p) .and. $associated(arr)) THEN + deallocate(arr) + END IF + end subroutine + + subroutine ap${cname}a7d$suffix(p, arr) + INTEGER :: p + $fname, $pointer, dimension(:,:,:,:,:,:,:) :: arr + integer :: n(7) + IF (fpup_isunpacking(p)) THEN + CALL fpup_ints(p,n,7) + If (n(1) >= 0) THEN + ALLOCATE(arr(n(1),n(2),n(3),n(4),n(5),n(6),n(7))) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + $NULLIFY + END If + ELSE ! packing + If ($associated(arr)) THEN + n(1)=SIZE(arr,DIM=1) + n(2)=SIZE(arr,DIM=2) + n(3)=SIZE(arr,DIM=3) + n(4)=SIZE(arr,DIM=4) + n(5)=SIZE(arr,DIM=5) + n(6)=SIZE(arr,DIM=6) + n(7)=SIZE(arr,DIM=7) + CALL fpup_ints(p,n,7) + call fpup_${pupnames}(p, arr, size(arr)) + ELSE + n(1) = -1 + n(2) = -1 + n(3) = -1 + n(4) = -1 + n(5) = -1 + n(6) = -1 + n(7) = -1 + CALL fpup_ints(p,n,7) + End If + END IF + IF (fpup_isdeleting(p) .and. $associated(arr)) THEN + deallocate(arr) + END IF + end subroutine + +END_OF_DATATYPE + + done +done + +echo " end module" >> pup_f.f90 + diff --git a/src/pup/pup_mpi.h b/src/pup/pup_mpi.h new file mode 100644 index 0000000..9d78ff6 --- /dev/null +++ b/src/pup/pup_mpi.h @@ -0,0 +1,68 @@ +/* +PUP -> MPI interface routines + + +Orion Sky Lawlor, olawlor@acm.org, 2004/9/15 +*/ +#ifndef __UIUC_CHARM_PUP_MPI_H +#define __UIUC_CHARM_PUP_MPI_H + +#include "pup.h" +#include "mpi.h" + +#define pup_checkMPI(err) pup_checkMPIerr(err,__FILE__,__LINE__); +inline void pup_checkMPIerr(int mpi_err,const char *file,int line) { + if (mpi_err!=MPI_SUCCESS) { + CmiError("MPI Routine returned error %d at %s:%d\n", + mpi_err,file,line); + CmiAbort("MPI Routine returned error code"); + } +} + +/// Return the number of dt's in the next MPI message from/tag/comm. +inline int MPI_Incoming_pup(MPI_Datatype dt,int from,int tag,MPI_Comm comm) { + MPI_Status sts; + pup_checkMPI(MPI_Probe(from,tag,comm,&sts)); + int len; pup_checkMPI(MPI_Get_count(&sts,dt,&len)); + return len; +} + +/// MPI_Recv, but using an object T with a pup routine. +template +inline void MPI_Recv_pup(T &t, int from,int tag,MPI_Comm comm) { + int len=MPI_Incoming_pup(MPI_BYTE,from,tag,comm); + MPI_Status sts; + char *buf=new char[len]; + pup_checkMPI(MPI_Recv(buf,len,MPI_BYTE, from,tag,comm,&sts)); + PUP::fromMemBuf(t,buf,len); + delete[] buf; +} + +/// MPI_Send, but using an object T with a pup routine. +template +inline void MPI_Send_pup(T &t, int to,int tag,MPI_Comm comm) { + size_t len=PUP::size(t); char *buf=new char[len]; + PUP::toMemBuf(t,buf,len); + pup_checkMPI(MPI_Send(buf,len,MPI_BYTE, to,tag,comm)); + delete[] buf; +} + +/// MPI_Bcast, but using an object T with a pup routine. +template +inline void MPI_Bcast_pup(T &t, int root,MPI_Comm comm) { + int myRank; + MPI_Comm_rank(comm,&myRank); + /* Can't do broadcast until everybody knows the size */ + size_t len=0; + if(myRank == root) len=PUP::size(t); + pup_checkMPI(MPI_Bcast(&len,1,MPI_INT,root,comm)); + /* Now pack object and send off */ + char *buf=new char[len]; + if(myRank == root) PUP::toMemBuf(t,buf,len); + pup_checkMPI(MPI_Bcast(buf,len,MPI_BYTE, root,comm)); + PUP::fromMemBuf(t,buf,len); + delete [] buf; +} + + +#endif diff --git a/src/pup/pup_paged.C b/src/pup/pup_paged.C new file mode 100644 index 0000000..29c4eaa --- /dev/null +++ b/src/pup/pup_paged.C @@ -0,0 +1,141 @@ +#include "converse.h" +#include "pup_paged.h" + + + +pup_pagetable *getNewPagetable(char *fName){ + pup_pagetable *_pagetable = new pup_pagetable; + _pagetable->freelist = NULL; + _pagetable->table = NULL; + _pagetable->tailtable = NULL; + _pagetable->maxblk=0; + int len = strlen(fName)+20; + _pagetable->fName = new char[len]; + snprintf(_pagetable->fName,len,"%s_%d.dat",fName,CmiMyPe()); + _pagetable->fp = fopen(_pagetable->fName,"wb"); + fclose(_pagetable->fp); + _pagetable->fp = fopen(_pagetable->fName,"r+b"); + + return _pagetable; +} + +void PUP_toPagedDisk::addpageentry(){ + entry = new pup_pageentry; + entry->next = NULL; + entry->ptr = handle; + entry->blklist = NULL; + tailblklist = NULL; + if(_pagetable->tailtable == NULL){ + _pagetable->table = entry; + }else{ + _pagetable->tailtable->next = entry; + } + _pagetable->tailtable = entry; +} + +void PUP_toPagedDisk::nextblock(){ + pup_list *f; + f = _pagetable->freelist; + if(f != NULL){ + current_block = f->n; + _pagetable->freelist=f->next; + delete f; + }else{ + current_block = _pagetable->maxblk; + _pagetable->maxblk = current_block+1; + } + pup_list *newblk = new pup_list; + newblk->n = current_block; + newblk->next = NULL; + if(tailblklist == NULL){ + entry->blklist = newblk; + }else{ + tailblklist->next = newblk; + } + tailblklist = newblk; + bytes_left = PUP_BLOCK; +} + + + +void PUP_toPagedDisk::bytes(void *p,size_t n,size_t itemSize,PUP::dataType) { + long size = itemSize*n; + char *c = (char *)p; + while(size > bytes_left){ + long next=current_block*PUP_BLOCK + PUP_BLOCK - bytes_left; + fseek(fp,next,SEEK_SET); + fwrite(c,1,bytes_left,fp); + size -= bytes_left; + c += bytes_left; + bytes_left = 0; + nextblock(); + } + long next=current_block*PUP_BLOCK + PUP_BLOCK - bytes_left; + fseek(fp,next,SEEK_SET); + fwrite(c,1,size,fp); + bytes_left -= size; +} + + + +void PUP_fromPagedDisk::findpageentry(){ + pup_pageentry *p; + p = NULL; + entry = _pagetable->table; + while(entry != NULL && entry->ptr != handle){ + p = entry; + entry = entry->next; + } + if( p == NULL){ + _pagetable->table = entry->next; + }else{ + p->next = entry->next; + } + if(_pagetable->tailtable == entry){ + _pagetable->tailtable = p; + } +} + +void PUP_fromPagedDisk::nextblock(){ + if(current_block != -1){ + // add blocks to the free list; + pup_list *freenode = new pup_list; + freenode->n = current_block; + freenode->next = NULL; + if(_pagetable->freelist == NULL){ + _pagetable->freelist = freenode; + _pagetable->tailfreelist = freenode; + }else{ + _pagetable->tailfreelist->next = freenode; + _pagetable->tailfreelist = freenode; + } + } + if(entry->blklist != NULL){ + current_block = entry->blklist->n; + entry->blklist = entry->blklist->next; + } + bytes_unread = PUP_BLOCK; +} + +void PUP_fromPagedDisk::bytes(void *p,size_t n,size_t itemSize,PUP::dataType ){ + long size = n*itemSize; + char *c = (char *)p; + while(size > bytes_unread){ + long next = current_block*PUP_BLOCK + PUP_BLOCK - bytes_unread; + fseek(fp,next,SEEK_SET); + if (fread(c,1,bytes_unread,fp) != bytes_unread) { + CmiAbort("PUP> reading bytes from disk failed!"); + } + size -= bytes_unread; + c += bytes_unread; + bytes_unread = 0; + nextblock(); + } + long next = current_block*PUP_BLOCK + PUP_BLOCK - bytes_unread; + fseek(fp,next,SEEK_SET); + if (fread(c,1,size,fp) != size) { + CmiAbort("PUP> reading bytes from disk failed!"); + } + bytes_unread -= size; +} + diff --git a/src/pup/pup_paged.h b/src/pup/pup_paged.h new file mode 100644 index 0000000..1d6dc51 --- /dev/null +++ b/src/pup/pup_paged.h @@ -0,0 +1,84 @@ +#ifndef _PUP_PAGED_H_ +#define _PUP_PAGED_H_ +#define PUP_BLOCK 512 +#include +#include +#include "pup.h" +typedef struct _list{ + int n; + struct _list *next; +} pup_list; +// each pageentry is indexed by the pointer of the object +typedef struct _pageentry{ + void *ptr; + pup_list *blklist; + struct _pageentry *next; +} pup_pageentry; + +typedef struct { + pup_list *freelist; + pup_list *tailfreelist; //tail of freelist + pup_pageentry *table; + pup_pageentry *tailtable; // tail of pagetable entries + int maxblk; // the number of blocks that have been written out by now + FILE *fp; + char *fName; +} pup_pagetable; + + +pup_pagetable *getNewPagetable(char *fName); + +class PUP_pagedDisk : public PUP::er { + protected: + pup_pagetable *_pagetable; + void *handle; // handle of the object to be restored + PUP_pagedDisk(unsigned int type,void *objhandle,pup_pagetable *pgtable):PUP::er(type),_pagetable(pgtable),handle(objhandle){ + } + +}; + +class PUP_toPagedDisk : public PUP_pagedDisk{ + protected: + virtual void bytes(void *p,size_t n,size_t itemSize,PUP::dataType t); + pup_pageentry *entry; + long current_block; + long bytes_left; + FILE *fp; + pup_list *tailblklist; + public: + PUP_toPagedDisk(void *objhandle,pup_pagetable *pgtable):PUP_pagedDisk(IS_PACKING,objhandle,pgtable){ + addpageentry(); + nextblock(); + fp = _pagetable->fp; + } + + void addpageentry(); + void nextblock(); + + +}; + +class PUP_fromPagedDisk : public PUP_pagedDisk{ + protected: + virtual void bytes(void *p,size_t n,size_t itemSize,PUP::dataType ); + pup_pageentry *entry; + long current_block; + long bytes_unread; + FILE *fp; + public: + PUP_fromPagedDisk(void *objhandle,pup_pagetable *pgtable):PUP_pagedDisk(IS_UNPACKING,objhandle,pgtable){ + findpageentry(); + current_block = -1; + nextblock(); + fp = _pagetable->fp; + } + + ~PUP_fromPagedDisk(){ + nextblock(); + delete entry; + } + void findpageentry(); + void nextblock(); +}; + +#endif diff --git a/src/pup/pup_stl.h b/src/pup/pup_stl.h new file mode 100644 index 0000000..7e06b12 --- /dev/null +++ b/src/pup/pup_stl.h @@ -0,0 +1,701 @@ +/* +Pup routines for STL classes. + +After including this header, you can parameter-marshall +a variable consisting of STL containers such as vectors, +lists, maps, strings, or pairs. + +This includes variables of type "std::list", or even +"std::map >". + +NOT included are the rarer types like valarray or slice. + +Orion Sky Lawlor, olawlor@acm.org, 7/22/2002 +*/ +#ifndef _UIUC_CHARM_PUP_STL_H +#define _UIUC_CHARM_PUP_STL_H + +#include + +/*It's kind of annoying that we have to drag all these headers in + just so the std:: parameter declarations will compile. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /*for std::pair*/ +#include +#include "pup.h" + +#include + +namespace PUP { + /*************** Simple classes ***************/ + // Non-const version is required for puping std::pair + template + inline void operator|(er &p,typename std::pair &v); + template + inline void operator|(er &p,typename std::pair &v); + template + inline void operator|(er &p,std::complex &v); + template + inline void operator|(er &p, std::shared_ptr &t); + template + inline void operator|(er &p, std::unique_ptr> &ptr); + template + inline void operator|(er &p,typename std::basic_string &v); + inline void operator|(er &p,std::string &v); + template + inline size_t PUP_stl_container_size(er &p,container &c); + template + inline void PUP_stl_container_items(er &p, container &c, size_t nElem); + template <> inline void PUP_stl_container_items,bool>(er &p, std::vector &c, size_t nElem); + template + inline void PUP_stl_container(er &p,container &c); + template + inline void PUP_stl_map(er &p,container &c); + template + inline void operator|(er &p,typename std::vector &v); + template + inline void operator|(er &p,typename std::deque &d); + template + inline void operator|(er &p,typename std::list &v); + template + inline void operator|(er &p,typename std::forward_list &fl); + template + inline void operator|(er &p,typename std::map &m); + template + inline void operator|(er &p,typename std::multimap &m); + template + inline void operator|(er &p,typename std::set &m); + template + inline void operator|(er &p,typename std::multiset &m); + template <> inline void operator|(er &p,std::vector &v); + + template + inline void operator|(er &p,typename std::pair &v) + { + p.syncComment(sync_index); + p|v.first; + p.syncComment(sync_item); + p|v.second; + } + // Const version is required for puping std::map + template + inline void operator|(er &p,typename std::pair &v) + { + p.syncComment(sync_index); + p|*(A *)&v.first; /* cast away constness on A */ + p.syncComment(sync_item); + p|v.second; + } + template + inline void operator|(er &p,std::complex &v) + { + T re=v.real(), im=v.imag(); + p|re; p|im; + v=std::complex(re,im); + } + template + inline void operator|(er &p,typename std::basic_string &v) + { + size_t nChar=v.length(); + p|nChar; + if (p.isUnpacking()) { //Unpack to temporary buffer + charType *buf=new charType[nChar]; + p(buf,nChar); + v=std::basic_string(buf,nChar); + delete[] buf; + } + else /*packing*/ { //Do packing in-place from data + //Have to cast away constness here + p((charType *)v.data(),nChar); + } + } + inline void operator|(er &p,std::string &v) + { + p.syncComment(sync_begin_object,"std::string"); + size_t nChar=v.length(); + p|nChar; + if (p.isUnpacking()) { //Unpack to temporary buffer + char *buf=new char[nChar]; + CmiEnforce(buf != nullptr); + p(buf,nChar); + v=std::basic_string(buf,nChar); + delete[] buf; + } + else /*packing*/ { //Do packing in-place from data + //Have to cast away constness here + p((char *)v.data(),nChar); + } + p.syncComment(sync_end_object); + } + + /**************** Containers *****************/ + + template + inline void reserve_if_applicable(container &c, size_t nElem) + { + c.clear(); + c.reserve(nElem); + } + template + inline void reserve_if_applicable(std::deque &c, size_t nElem) + { + c.clear(); + } + template + inline void reserve_if_applicable(std::list &c, size_t nElem) + { + c.clear(); + } + template + inline void reserve_if_applicable(std::set &c, size_t nElem) + { + c.clear(); + } + template + inline void reserve_if_applicable(std::multiset &c, size_t nElem) + { + c.clear(); + } + template + inline void reserve_if_applicable(std::map &c, size_t nElem) + { + c.clear(); + } + template + inline void reserve_if_applicable(std::multimap &c, size_t nElem) + { + c.clear(); + } + + template + inline void emplace(container &c, Args&&... args) + { + c.emplace(std::forward(args)...); + } + template + inline void emplace(std::vector &c, Args&&... args) + { + c.emplace_back(std::forward(args)...); + } + template + inline void emplace(std::deque &c, Args&&... args) + { + c.emplace_back(std::forward(args)...); + } + template + inline void emplace(std::list &c, Args&&... args) + { + c.emplace_back(std::forward(args)...); + } + + //Impl. util: pup the length of a container + template + inline size_t PUP_stl_container_size(er &p,container &c) { + size_t nElem=c.size(); + p|nElem; + return nElem; + } + + //Impl. util: pup each current item of a container (no allocation) + template + inline void PUP_stl_container_items(er &p, container &c, size_t nElem) + { + if (p.isUnpacking()) + { + reserve_if_applicable(c, nElem); + for (size_t i = 0; i < nElem; ++i) + { + p.syncComment(sync_item); + detail::TemporaryObjectHolder n; + p|n; + emplace(c, std::move(n.t)); + } + } + else + { + for (typename container::iterator it=c.begin(); it!=c.end(); ++it) + { + p.syncComment(sync_item); + // Cast away the constness (needed for std::set) + p|*(dtype *)&(*it); + } + } + } + + // Specialized to work with vector + template<> + inline void PUP_stl_container_items, bool>(er &p, std::vector &c, size_t nElem) + { + // iterators of std::vector are read-only temporaries so we need special handling for unpacking + if (p.isUnpacking()) { + c.resize(nElem); + for (size_t i = 0; i < nElem; ++i) + { + p.syncComment(sync_item); + detail::TemporaryObjectHolder n; + p|n; + c[i] = n.t; + } + } + else { + for (bool n : c) + { + p.syncComment(sync_item); + p|n; + } + } + } + + template + inline void PUP_stl_container(er &p,container &c) { + p.syncComment(sync_begin_array); + size_t nElem=PUP_stl_container_size(p,c); + PUP_stl_container_items(p, c, nElem); + p.syncComment(sync_end_array); + } + + // forward_list does not have: .size(), .emplace(), .emplace_back() + template + inline void PUP_stl_forward_list(er &p,std::forward_list &c) { + p.syncComment(sync_begin_array); + size_t nElem; + if (p.isUnpacking()) + { + p | nElem; + auto iter = c.before_begin(); + for (size_t i = 0; i < nElem; ++i) + { + p.syncComment(sync_item); + detail::TemporaryObjectHolder n; + p|n; + iter = c.emplace_after(iter, std::move(n.t)); + } + } + else + { + nElem = 0; + for (auto& n: c) + { + ++nElem; + } + p | nElem; + for (auto& n : c) + { + p.syncComment(sync_item); + p | n; + } + } + p.syncComment(sync_end_array); + } + + template + inline void PUP_stl_map(er &p,container &c) { + p.syncComment(sync_begin_list); + size_t nElem=PUP_stl_container_size(p,c); + if (p.isUnpacking()) + { //Unpacking: Extract each element and insert: + reserve_if_applicable(c, nElem); + for (size_t i=0;i k; + detail::TemporaryObjectHolder v; + + // keep in sync with std::pair + p.syncComment(sync_index); + p | k; + p.syncComment(sync_item); + p | v; + + c.emplace(std::piecewise_construct, std::forward_as_tuple(std::move(k.t)), std::forward_as_tuple(std::move(v.t))); + } + } + else + { + for (auto& kv : c) + { + p | kv; + } + } + p.syncComment(sync_end_list); + } + + template + inline void operator|(er &p, typename std::vector &v) { + if (PUP::as_bytes::value) { + size_t nElem = PUP_stl_container_size(p, v); + if (p.isUnpacking()) { + v.resize(nElem); + v.shrink_to_fit(); + } + PUParray(p, v.data(), nElem); + } else { + PUP_stl_container, T>(p, v); + } + } + + template + inline void operator|(er &p,typename std::deque &d) + { PUP_stl_container,T>(p,d); } + template + inline void operator|(er &p,typename std::list &v) + { PUP_stl_container,T>(p,v); } + template + inline void operator|(er &p,typename std::forward_list &fl) + { PUP_stl_forward_list(p,fl); } + + template + inline void operator|(er &p,typename std::map &m) + { PUP_stl_map,V,T >(p,m); } + template + inline void operator|(er &p,typename std::multimap &m) + { PUP_stl_map,V,T >(p,m); } + /// \warning This does not work with custom hash functions that have state + template + inline void operator|(er &p,typename std::unordered_map &m) + { PUP_stl_map,V,T >(p,m); } + template + inline void operator|(er &p,typename std::unordered_multimap &m) + { PUP_stl_map,V,T >(p,m); } + + template + inline void operator|(er &p,typename std::set &m) + { PUP_stl_container,T >(p,m); } + template + inline void operator|(er &p,typename std::multiset &m) + { PUP_stl_container,T >(p,m); } + template + inline void operator|(er &p,typename std::unordered_set &m) + { PUP_stl_container,T >(p,m); } + template + inline void operator|(er &p,typename std::unordered_multiset &m) + { PUP_stl_container,T >(p,m); } + + // Specialized to work with vector, which doesn't + // have data() or shrink_to_fit() members + template <> + inline void operator|(er &p,std::vector &v) { + PUP_stl_container, bool>(p, v); + } + +// Distributed under the MIT License. +// The following allows for pupping STL structures with pointers to abstract +// base classes. Requires is used in place of enable_if_t to enforce +// requirements on template parameters for the following PUP methods. +template +struct requires_impl { + using template_error_type_failed_to_meet_requirements_on_template_parameters + = std::nullptr_t; +}; + +template <> +struct requires_impl {}; + +template +using Requires = typename requires_impl< + B>::template_error_type_failed_to_meet_requirements_on_template_parameters; + + template ::value> = nullptr> + inline void pup(PUP::er& p, std::array& a) { + std::for_each(a.begin(), a.end(), [&p](T& t) { p | t; }); + } + + template ::value> = nullptr> + inline void pup(PUP::er& p, std::array& a) { + PUParray(p, a.data(), N); + } + + template + inline void operator|(er& p, std::array& a) { + pup(p, a); + } + + template ::value> = nullptr> + inline void operator|(PUP::er& p, T& s) { + pup_bytes(&p, static_cast(&s), sizeof(T)); + } + + template = nullptr> + void pup_tuple_impl(PUP::er& /* p */, std::tuple& /* t */) { + } + + template = nullptr> + void pup_tuple_impl(PUP::er& p, std::tuple& t) { + p | std::get(t); + } + + template 0 && N > 0)> = nullptr> + void pup_tuple_impl(PUP::er& p, std::tuple& t) { + p | std::get(t); + pup_tuple_impl(p, t); + } + + template + inline void pup(PUP::er& p, std::tuple& t) { + pup_tuple_impl(p, t); + } + + template + inline void operator|(PUP::er& p, std::tuple& t) { + pup(p, t); + } + + template ::value> = nullptr> + inline void pup(PUP::er& p, std::unique_ptr& t) { + T* t1 = t.get(); + PUP::ptr_helper()(p, t1); + if (p.isUnpacking()) { + t.reset(t1); + } + } + + template ::value> = nullptr> + inline void pup(PUP::er& p, std::unique_ptr& t) { + PUP::able* t1 = nullptr; + if (p.isUnpacking()) { + p | t1; + t.reset(dynamic_cast(t1)); + } else { + t1 = dynamic_cast(t.get()); + p | t1; + } + } + + template + inline void operator|(PUP::er& p, std::unique_ptr& t) { + pup(p, t); + } + + template ::value> = nullptr> + inline void pup(PUP::er& p, std::shared_ptr& t) { + T* t1 = t.get(); + PUP::ptr_helper()(p, t1); + if (p.isUnpacking()) { + t.reset(t1); + } + } + + template ::value> = nullptr> + inline void pup(PUP::er &p, std::shared_ptr &t) { + PUP::able* _ = (p.isUnpacking()) ? nullptr : t.get(); + p(&_); + if (p.isUnpacking()) { + // the shared ptr must be created with the original PUP::able ptr + // otherwise the static cast can lead to invalid frees + // (since it can change the pointer's value) + t = std::static_pointer_cast(std::shared_ptr(_)); + } + } + + template + inline void operator|(PUP::er& p, std::shared_ptr& t) { + pup(p, t); + } + + + //Adding random numberengines defined in the header . + //To pup an engine we need to pup it's state and create a new engine with the same state after unpacking + template< + class UIntType, size_t w, size_t n, size_t m, size_t r, + UIntType a, size_t u, UIntType d, size_t s,UIntType b, size_t t,UIntType c, size_t l, UIntType f> + inline void pup(PUP::er& p, std::mersenne_twister_engine& engine){ + std::stringstream o; + std::string state; + + if(p.isUnpacking()){ + p | state; + o.str(state); + o>>engine; + } + else{ + o< + inline void operator|(PUP::er& p, std::mersenne_twister_engine& engine) { + pup(p,engine); + } + + template + inline void pup(PUP::er& p, std::linear_congruential_engine& engine){ + std::stringstream o; + std::string state; + + if(p.isUnpacking()){ + p | state; + o.str(state); + o>>engine; + } + else{ + o< + inline void operator|(PUP::er& p, std::linear_congruential_engine& engine) { + pup(p,engine); + } + + template + inline void pup(PUP::er& p, std::subtract_with_carry_engine& engine){ + std::stringstream o; + std::string state; + + if(p.isUnpacking()){ + p | state; + o.str(state); + o>>engine; + } + else{ + o< + inline void operator|(PUP::er& p, std::subtract_with_carry_engine& engine) { + pup(p,engine); + } + + template + inline void pup(PUP::er& p, std::discard_block_engine& engine) { + std::stringstream o; + std::string state; + + if(p.isUnpacking()){ + p | state; + o.str(state); + o>>engine; + } + else{ + o< + inline void operator|(PUP::er& p, std::discard_block_engine& engine) { + pup(p,engine); + } + + template + inline void pup(PUP::er& p, std::independent_bits_engine& engine) { + std::stringstream o; + std::string state; + + if(p.isUnpacking()){ + p | state; + o.str(state); + o>>engine; + } + else{ + o< + inline void operator|(PUP::er& p, std::independent_bits_engine& engine) { + pup(p,engine); + } + + template + inline void pup(PUP::er& p, std::shuffle_order_engine& engine) { + std::stringstream o; + std::string state; + + if(p.isUnpacking()){ + p | state; + o.str(state); + o>>engine; + } + else{ + o< + inline void operator|(PUP::er& p, std::shuffle_order_engine& engine) { + pup(p,engine); + } + + template + inline void pup(PUP::er& p, std::chrono::duration& duration) + { + Rep count; + if (p.isUnpacking()) + { + p | count; + duration = std::chrono::duration(count); + } + else + { + count = duration.count(); + p | count; + } + } + + template + inline void operator|(PUP::er& p, std::chrono::duration& duration) + { + pup(p, duration); + } + + template + inline void pup(PUP::er& p, std::chrono::time_point& tp) + { + Duration sinceEpoch; + if (p.isUnpacking()) + { + p | sinceEpoch; + tp = std::chrono::duration(sinceEpoch); + } + else + { + sinceEpoch = tp.time_since_epoch(); + p | sinceEpoch; + } + } + + template + inline void operator|(PUP::er& p, std::chrono::time_point& tp) + { + pup(p, tp); + } +} // end of namespace PUP + +#endif diff --git a/src/pup/pup_toNetwork.C b/src/pup/pup_toNetwork.C new file mode 100644 index 0000000..e65ae03 --- /dev/null +++ b/src/pup/pup_toNetwork.C @@ -0,0 +1,145 @@ +/* +Implementation of pup_toNetwork.h + +Orion Sky Lawlor, olawlor@acm.org, 2004/3/18 +*/ +#include "converse.h" +#include "pup.h" +#include "pup_toNetwork.h" +#include +#include +#include +#include + +/****************** toNetwork ******************** + */ + +void PUP_toNetwork_sizer::bytes(void *p, size_t n, size_t itemSize, + PUP::dataType t) { + switch (t) { + case PUP::Tchar: // Strings and bytes get copied as-is + case PUP::Tuchar: + case PUP::Tbyte: + nBytes += n; + break; + case PUP::Tulong: // longs and doubles are 8 bytes + case PUP::Tlong: + case PUP::Tulonglong: + case PUP::Tlonglong: + case PUP::Tdouble: + case PUP::Tlongdouble: + nBytes += n * 8; + break; + case PUP::Tpointer: // pointers are 8 bytes on 64-bit machines and 4 bytes on + // 32-bit machines + nBytes += n * sizeof(void *); + break; + default: // Everything else goes as a network int + nBytes += n * 4; + break; + } +} + +void PUP_toNetwork_sizer::pup_buffer(void *&p, size_t n, size_t itemSize, + PUP::dataType t) { + bytes(p, n, itemSize, t); +} + +void PUP_toNetwork_sizer::pup_buffer(void *&p, size_t n, size_t itemSize, + PUP::dataType t, + std::function allocate, + std::function deallocate) { + bytes(p, n, itemSize, t); +} + +#define casesPUP_toNetwork_types \ + casePUP_toNetwork_type(Tfloat, float, float); \ + casePUP_toNetwork_type(Tdouble, double, double); \ + case PUP::Tushort: /* Fallthrough (no special treatment for unsigned) */ \ + casePUP_toNetwork_type(Tshort, short, int); \ + case PUP::Tuint: \ + casePUP_toNetwork_type(Tint, int, int); \ + case PUP::Tulong: \ + casePUP_toNetwork_type(Tlong, long, CMK_NETWORK_INT8); \ + case PUP::Tulonglong: \ + casePUP_toNetwork_type(Tlonglong, CMK_NETWORK_INT8, CMK_NETWORK_INT8); \ + casePUP_toNetwork_type(Tbool, bool, int); \ + case PUP::Tsync: \ + break; /* ignore syncs */ \ + casePUP_toNetwork_type(Tpointer, void *, CMK_POINTER_SIZED_INT); + +void PUP_toNetwork_pack::bytes(void *p, size_t n, size_t itemSize, + PUP::dataType t) { + size_t i; + switch (t) { + case PUP::Tchar: // Strings and bytes get copied as-is + case PUP::Tuchar: + case PUP::Tbyte: + memcpy(buf, p, n); + buf += n; + break; +#define casePUP_toNetwork_type(enumName, typeName, writeAs) \ + case PUP::enumName: \ + for (i = 0; i < n; i++) \ + w((writeAs)((uintptr_t)((typeName *)p)[i])); \ + break + casesPUP_toNetwork_types +#if CMK_LONG_DOUBLE_DEFINED + casePUP_toNetwork_type(Tlongdouble, long double, double); +#endif +#undef casePUP_toNetwork_type + + default: + CmiAbort("Unrecognized type passed to PUP_toNetwork_pack!\n"); + } +} + +void PUP_toNetwork_pack::pup_buffer(void *&p, size_t n, size_t itemSize, + PUP::dataType t) { + bytes(p, n, itemSize, t); +} + +void PUP_toNetwork_pack::pup_buffer(void *&p, size_t n, size_t itemSize, + PUP::dataType t, + std::function allocate, + std::function deallocate) { + bytes(p, n, itemSize, t); +} + +void PUP_toNetwork_unpack::bytes(void *p, size_t n, size_t itemSize, + PUP::dataType t) { + size_t i; + switch (t) { + case PUP::Tchar: // Strings and bytes get copied as-is + case PUP::Tuchar: + case PUP::Tbyte: + memcpy(p, buf, n); + buf += n; + break; +#define casePUP_toNetwork_type(enumName, typeName, readAs) \ + case PUP::enumName: \ + for (i = 0; i < n; i++) \ + ((typeName *)p)[i] = (typeName)read_##readAs(); \ + break + casesPUP_toNetwork_types +#if CMK_LONG_DOUBLE_DEFINED + casePUP_toNetwork_type(Tlongdouble, long double, double); +#endif +#undef casePUP_toNetwork_type + + default: + CmiAbort("Unrecognized type passed to PUP_toNetwork_unpack!\n"); + } +} + +void PUP_toNetwork_unpack::pup_buffer(void *&p, size_t n, size_t itemSize, + PUP::dataType t) { + bytes(p, n, itemSize, t); +} + +void PUP_toNetwork_unpack::pup_buffer(void *&p, size_t n, size_t itemSize, + PUP::dataType t, + std::function allocate, + std::function deallocate) { + bytes(p, n, itemSize, t); +} diff --git a/src/pup/pup_toNetwork.h b/src/pup/pup_toNetwork.h new file mode 100644 index 0000000..7c5663a --- /dev/null +++ b/src/pup/pup_toNetwork.h @@ -0,0 +1,132 @@ +/* +Pack/unpack to network-byte-order integer/float/double. +On-the-wire sizes correspond to Java sizes for +int, long, float, and double. + +Orion Sky Lawlor, olawlor@acm.org, 11/1/2001 + */ +#ifndef __OSL_PUP_TONETWORK_H +#define __OSL_PUP_TONETWORK_H + +#include "pup.h" + + +typedef CMK_TYPEDEF_INT4 CMK_NETWORK_INT4; +#ifdef CMK_PUP_LONG_LONG +typedef CMK_PUP_LONG_LONG CMK_NETWORK_INT8; +#else +typedef CMK_TYPEDEF_INT8 CMK_NETWORK_INT8; /* long is 8 bytes */ +#endif + +/// Integer type the same size as "float" +typedef CMK_NETWORK_INT4 CMK_FLOAT_SIZED_INT; +/// Integer type the same size as "double" +typedef CMK_NETWORK_INT8 CMK_DOUBLE_SIZED_INT; + +#if CMK_SIZET_64BIT +typedef CMK_NETWORK_INT8 CMK_POINTER_SIZED_INT; +#else +typedef CMK_NETWORK_INT4 CMK_POINTER_SIZED_INT; +#endif + +class PUP_toNetwork_sizer : public PUP::er { + size_t nBytes; + virtual void bytes(void *p,size_t n,size_t itemSize,PUP::dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, PUP::dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, PUP::dataType t, std::function allocate, std::function deallocate); + + public: + PUP_toNetwork_sizer(void) :PUP::er(IS_SIZING), nBytes(0) {} + size_t size(void) const {return nBytes;} +}; + +class PUP_toNetwork_pack : public PUP::er { + unsigned char *buf,*start; + inline void w(CMK_NETWORK_INT4 i) { + //Write big-endian integer to output stream + *buf++=(unsigned char)(i>>24); //High end first + *buf++=(unsigned char)(i>>16); + *buf++=(unsigned char)(i>>8); + *buf++=(unsigned char)(i>>0); //Low end last + } + inline void w(CMK_NETWORK_INT8 i) { + w(CMK_NETWORK_INT4(i>>32)); + w(CMK_NETWORK_INT4(i)); + } + // NOTE: Need to use a union to force gcc compiler to consider pointer aliasing + //Write floating-point number to stream. + inline void w(float f) { + union { float f; CMK_FLOAT_SIZED_INT i; } uaw; + uaw.f=f; + w(uaw.i); + //w(*(CMK_FLOAT_SIZED_INT *)&f); + } + //Write floating-point number to stream. + inline void w(double f) { + union { double f; CMK_DOUBLE_SIZED_INT i; } uaw; + uaw.f=f; + w(uaw.i); + //w(*(CMK_DOUBLE_SIZED_INT *)&f); + } + + virtual void bytes(void *p,size_t n,size_t itemSize,PUP::dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, PUP::dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, PUP::dataType t, std::function allocate, std::function deallocate); + +public: + PUP_toNetwork_pack(void *dest) :PUP::er(IS_PACKING) { + start=buf=(unsigned char *)dest; + CmiAssert(sizeof(void *) == sizeof(CMK_POINTER_SIZED_INT)); + } + inline size_t size(void) const {return buf-start;} +}; + +class PUP_toNetwork_unpack : public PUP::er { + const unsigned char *buf,*start; + inline CMK_NETWORK_INT4 read_int(void) { + //Read big-endian integer to output stream + CMK_NETWORK_INT4 ret=(buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|(buf[3]); + buf+=4; + return ret; + } + inline CMK_NETWORK_INT8 read_CMK_NETWORK_INT8(void) { + CMK_NETWORK_INT8 hi=0xffFFffFFu&(CMK_NETWORK_INT8)read_int(); + CMK_NETWORK_INT8 lo=0xffFFffFFu&(CMK_NETWORK_INT8)read_int(); + return (hi<<32)|(lo); + } + inline void read_integer(CMK_NETWORK_INT4 &i) { i=read_int(); } + inline void read_integer(CMK_NETWORK_INT8 &i) { i=read_CMK_NETWORK_INT8(); } + // NOTE: Need to use a union to force gcc compiler to consider pointer aliasing + inline float read_float(void) { + union { float f; CMK_FLOAT_SIZED_INT i; } uaw; + read_integer(uaw.i); + return uaw.f; + //CMK_NETWORK_INT4 i=read_int(); + //return *(float *)&i; + } + inline double read_double(void) { + union { double f; CMK_DOUBLE_SIZED_INT i; } uaw; + read_integer(uaw.i); + return uaw.f; + //CMK_NETWORK_INT8 i=read_CMK_NETWORK_INT8(); + //return *(double *)&i; + } + inline void * read_CMK_POINTER_SIZED_INT(void) { + CMK_POINTER_SIZED_INT i; + read_integer(i); + return *(void **)&i; + } + + virtual void bytes(void *p,size_t n,size_t itemSize,PUP::dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, PUP::dataType t); + virtual void pup_buffer(void *&p, size_t n, size_t itemSize, PUP::dataType t, std::function allocate, std::function deallocate); + + public: + PUP_toNetwork_unpack(const void *src) :PUP::er(IS_UNPACKING) { + start=buf=(const unsigned char *)src; + } + inline size_t size(void) const {return buf-start;} +}; + +#endif + diff --git a/src/pup/pup_toNetwork4.C b/src/pup/pup_toNetwork4.C new file mode 100644 index 0000000..6cc8007 --- /dev/null +++ b/src/pup/pup_toNetwork4.C @@ -0,0 +1,97 @@ +/* +Implementation of pup_toNetwork4.h + +Orion Sky Lawlor, olawlor@acm.org, 2004/3/18 +*/ +#include +#include +#include +#include "converse.h" +#include "pup.h" +#include "pup_toNetwork4.h" + +/****************** toNetwork4 ******************** +Pack/unpack to 4-byte network-byte-order integer/float. +This makes it easy to use platform-neutral on-the-wire types. +*/ + +void PUP_toNetwork4_sizer::bytes(void *p,size_t n,size_t itemSize,PUP::dataType t) +{ + switch (t) { + case PUP::Tchar: //Strings and bytes get copied as-is + case PUP::Tuchar: + case PUP::Tbyte: + nBytes+=n; + break; + default: //Everything else goes as a network int + nBytes+=n*4; + } +} + +void PUP_toNetwork4_pack::bytes(void *p,size_t n,size_t itemSize,PUP::dataType t) +{ + size_t i; + switch (t) { + case PUP::Tchar: //Strings and bytes get copied as-is + case PUP::Tuchar: + case PUP::Tbyte: + memcpy(buf,p,n); + buf+=n; + break; +#define casePUP_toNetwork4_type(enumName,typeName,writeAs) \ + case PUP::enumName: \ + for (i=0;i>24); //High end first + *buf++=(unsigned char)(i>>16); + *buf++=(unsigned char)(i>>8); + *buf++=(unsigned char)(i>>0); //Low end last + } + + virtual void bytes(void *p,size_t n,size_t itemSize,PUP::dataType t); + public: + PUP_toNetwork4_pack(void *dest) :PUP::er(IS_PACKING) { + start=buf=(unsigned char *)dest; + } + PUP_toNetwork4_pack(const PUP_toNetwork4_pack &p); //don't copy + void operator=(const PUP_toNetwork4_pack &p); // don't copy + inline size_t size(void) const {return buf-start;} +}; + +class PUP_toNetwork4_unpack : public PUP::er { + const unsigned char *buf,*start; + // NOTE: Need to use a union to force gcc compiler to consider pointer aliasing + inline float read_float(void) { + //Read floating-point number from stream. + // Assumes "int4" and "float" type have the + // same size and endianness (true on every current machine). + union { float f; CMK_TYPEDEF_INT4 i; } uaw; + uaw.i=read_int(); + return uaw.f; + } + inline int read_int(void) { + //Read big-endian integer to output stream + int ret=(buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|(buf[3]); + buf+=4; + return ret; + } + + virtual void bytes(void *p,size_t n,size_t itemSize,PUP::dataType t); + public: + PUP_toNetwork4_unpack(const void *src) :PUP::er(IS_UNPACKING) { + start=buf=(const unsigned char *)src; + } + PUP_toNetwork4_unpack(const PUP_toNetwork4_unpack &p); //don't copy + void operator=(const PUP_toNetwork4_unpack &p); // don't copy + inline size_t size(void) const {return buf-start;} +}; + +#endif + diff --git a/src/pup/pup_util.C b/src/pup/pup_util.C new file mode 100644 index 0000000..0762334 --- /dev/null +++ b/src/pup/pup_util.C @@ -0,0 +1,1121 @@ +/* +Pack/UnPack Library for UIUC Parallel Programming Lab +Orion Sky Lawlor, olawlor@uiuc.edu, 4/5/2000 + +This library allows you to easily pack an array, structure, +or object into a memory buffer or disk file, and then read +the object back later. The library will also handle translating +between different machine representations. + +This file is needed because virtual function definitions in +header files cause massive code bloat-- hence the PUP library +virtual functions are defined here. + +*/ +#include +#include +#include +#include +#include +#include + +#include "converse.h" +#include "pup.h" +#include + +#include "conv-rdma.h" +#if defined(_WIN32) +#include + +int pwrite(int fd, const void *buf, size_t nbytes, __int64 offset) { + __int64 ret = _lseek(fd, offset, SEEK_SET); + + if (ret == -1) { + return (-1); + } + return (_write(fd, buf, nbytes)); +} +#define NO_UNISTD_NEEDED +#endif + +#if defined(__PGIC__) +// PGI compilers define funny feature flags that lead to standard +// headers omitting this prototype + +extern "C" { + +extern ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); +} +#define NO_UNISTD_NEEDED +#endif + +#if !defined(NO_UNISTD_NEEDED) +#include +#endif + +PUP::er::~er() {} + +void PUP::er::operator()(able &a) { a.pup(*this); } + +void PUP::er::comment(const char *message) { /* ignored by default */ } + +const char *PUP::er::typeString() const { + if (isSizing()) + return "sizing"; + else if (isPacking()) + return "packing"; + else if (isUnpacking()) + return "unpacking"; + return "unknown"; +} + +void PUP::er::synchronize(unsigned int m) { /* ignored by default */ } + +/*define CK_CHECK_PUP to get type and bounds checking during Pack and unpack. +This checking substantially slows down PUPing, and increases the space +required by packed objects. It can save hours of debugging, however. +*/ +#ifdef CK_CHECK_PUP +static int bannerDisplayed = 0; +static void showBanner(void) { + bannerDisplayed = 1; + fprintf(stderr, "CK_CHECK_PUP pup routine checking enabled\n"); + CmiPrintf("CK_CHECK_PUP pup routine checking enabled\n"); +} + +class pupCheckRec { + unsigned char magic[4]; // Cannot use "int" because of alignment + unsigned char type; + unsigned char length[8]; + enum { pupMagic = 0xf36c5a21, typeMask = 0x75 }; + int getMagic(void) const { + return (magic[3] << 24) + (magic[2] << 16) + (magic[1] << 8) + magic[0]; + } + void setMagic(int v) { + for (int i = 0; i < 4; i++) + magic[i] = (v >> (8 * i)); + } + PUP::dataType getType(void) const { return (PUP::dataType)(type ^ typeMask); } + void setType(PUP::dataType v) { type = v ^ typeMask; } + size_t getLength(void) const { + size_t v = 0; + for (int i = 0; i < 8; i++) + v += (length[i] << (8 * i)); + return v; + } + void setLength(size_t v) { + for (int i = 0; i < 8; i++) + length[i] = (v >> (8 * i)); + } + + /*Compare the packed value (from us) and the unpacked value + (from the user). + */ + void compare(const char *kind, const char *why, int packed, + int unpacked) const { + if (packed == unpacked) + return; + // If we get here, there is an error in the user's pack/unpack routine + fprintf(stderr, + "CK_CHECK_PUP error!\nPacked %s (%d, or %08x) does " + "not equal unpacked value (%d, or %08x)!\nThis means %s\n", + kind, packed, packed, unpacked, unpacked, why); + CmiPrintf("CK_CHECK_PUP error! Run with debugger for more info.\n"); + // Invoke the debugger + abort(); + } + +public: + void write(PUP::dataType t, size_t n) { + if (!bannerDisplayed) + showBanner(); + setMagic(pupMagic); + type = t ^ typeMask; + setLength(n); + } + void check(PUP::dataType t, size_t n) const { + compare("magic number", + "you unpacked more than you packed, or the values were corrupted " + "during transport", + getMagic(), pupMagic); + compare("data type", "the pack and unpack paths do not match up", getType(), + t); + compare("length", "you may have forgotten to pup the array length", + getLength(), n); + } +}; +#endif + +void PUP::sizer::bytes(void * /*p*/, size_t n, size_t itemSize, + dataType /*t*/) { +#ifdef CK_CHECK_PUP + nBytes += sizeof(pupCheckRec); +#endif + nBytes += n * itemSize; +} + +/*Memory PUP::er's*/ +void PUP::toMem::bytes(void *p, size_t n, size_t itemSize, dataType t) { +#ifdef CK_CHECK_PUP + ((pupCheckRec *)buf)->write(t, n); + buf += sizeof(pupCheckRec); +#endif + n *= itemSize; + memcpy((void *)buf, p, n); + buf += n; +} +void PUP::fromMem::bytes(void *p, size_t n, size_t itemSize, dataType t) { +#ifdef CK_CHECK_PUP + ((pupCheckRec *)buf)->check(t, n); + buf += sizeof(pupCheckRec); +#endif + n *= itemSize; + memcpy(p, (const void *)buf, n); + buf += n; +} + +void PUP::sizer::pup_buffer(void *&p, size_t n, size_t itemSize, dataType t) { +#ifdef CK_CHECK_PUP + nBytes += sizeof(pupCheckRec); +#endif + nBytes += sizeof(CmiNcpyBuffer); +} + +void PUP::sizer::pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate) { + pup_buffer(p, n, itemSize, t); +} + +void PUP::toMem::pup_buffer(void *&p, size_t n, size_t itemSize, dataType t) { + pup_buffer(p, n, itemSize, t, malloc, free); +} + +void PUP::fromMem::pup_buffer_generic(void *&p, size_t n, size_t itemSize, + dataType t, + std::function allocate, + bool isMalloc) { +#ifdef CK_CHECK_PUP + ((pupCheckRec *)buf)->check(t, n); + buf += sizeof(pupCheckRec); +#endif + // Unpup a CmiNcpyBuffer object with the callback + CmiNcpyBuffer src; + + PUP::fromMem fMem(buf); + fMem | src; + CmiAssert(sizeof(src) == sizeof(CmiNcpyBuffer)); + + // Allocate only if inter-process + if (isUnpacking()) { + if (CmiNodeOf(src.pe) != CmiMyNode()) { + if (isMalloc) + p = malloc(n * itemSize); + else + p = allocate(n); + CmiNcpyBuffer dest(p, n * itemSize); + zcPupGet(src, dest); + } else { + p = (void *)src.ptr; + // Free the allocated zcPupSourceInfo + zcPupSourceInfo *srcInfo = (zcPupSourceInfo *)(src.ref); + delete srcInfo; + } + } + buf += sizeof(src); +} + +void PUP::fromMem::pup_buffer(void *&p, size_t n, size_t itemSize, dataType t) { + pup_buffer_generic(p, n, itemSize, t, malloc, true); +} + +void PUP::toMem::pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate) { +#ifdef CK_CHECK_PUP + ((pupCheckRec *)buf)->write(t, n); + buf += sizeof(pupCheckRec); +#endif + // Create a CmiNcpyBuffer object with the callback + CmiNcpyBuffer src(p, n * itemSize); + zcPupSourceInfo *srcInfo = zcPupAddSource(src, deallocate); + src.ref = srcInfo; + CmiAssert(sizeof(src) == sizeof(CmiNcpyBuffer)); + PUP::toMem tMem(buf); + tMem | src; + buf += sizeof(src); +} + +void PUP::fromMem::pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate) { + pup_buffer_generic(p, n, itemSize, t, allocate, false); +} + +extern "C" { + +int CmiOpen(const char *pathname, int flags, int mode) { + int fd = -1; + while (1) { +#if defined(_WIN32) + fd = _open(pathname, flags, mode); +#else + fd = open(pathname, flags, mode); +#endif + if (fd == -1 && errno == EINTR) { + CmiError("Warning: CmiOpen retrying on %s\n", pathname); + continue; + } else + break; + } + return fd; +} + +// dealing with short write +size_t CmiFwrite(const void *ptr, size_t size, size_t nmemb, FILE *f) { + size_t nwritten = 0; + const char *buf = (const char *)ptr; + double firsttime = 0; + while (nwritten < nmemb) { + size_t ncur = fwrite(buf + nwritten * size, size, nmemb - nwritten, f); + if (ncur <= 0) { + if (errno == EINTR) + CmiError("Warning: CmiFwrite retrying ...\n"); + else if (errno == ENOMEM) { + if (firsttime == 0) + firsttime = CmiWallTimer(); + if (CmiWallTimer() - firsttime > 300) + break; + } else + break; + } else + nwritten += ncur; + } + if (firsttime != 0) + CmiError("Warning: CmiFwrite retried for %lf ...\n", + CmiWallTimer() - firsttime); + + return nwritten; +} + +CmiInt8 CmiPwrite(int fd, const char *buf, size_t bytes, size_t offset) { + size_t origBytes = bytes; + while (bytes > 0) { + CmiInt8 ret = pwrite(fd, buf, bytes, offset); + if (ret < 0) { + if (errno == EINTR) { + continue; + } else { + return ret; + } + } + bytes -= ret; + buf += ret; + offset += ret; + } + return origBytes; +} + +size_t CmiFread(void *ptr, size_t size, size_t nmemb, FILE *f) { + size_t nread = 0; + char *buf = (char *)ptr; + while (nread < nmemb) { + size_t ncur = fread(buf + nread * size, size, nmemb - nread, f); + if (ncur <= 0) { + if (errno == EINTR) + CmiError("Warning: CmiFread retrying ...\n"); + else + break; + } else + nread += ncur; + } + return nread; +} + +FILE *CmiFopen(const char *path, const char *mode) { + FILE *fp = NULL; + while (1) { + fp = fopen(path, mode); + if (fp == 0 && errno == EINTR) { + CmiError("Warning: CmiFopen retrying on %s\n", path); + continue; + } else + break; + } + return fp; +} + +// more robust fclose that handling interrupt +int CmiFclose(FILE *fp) { + int status = 0; + while (1) { + status = fflush(fp); + if (status != 0 && errno == EINTR) { + CmiError("Warning: CmiFclose flush retrying ...\n"); + continue; + } else + break; + } + if (status != 0) + return status; + while (1) { + status = fclose(fp); + if (status != 0 && errno == EINTR) { + CmiError("Warning: CmiFclose retrying ...\n"); + continue; + } else + break; + } + return status; +} + +} // extern "C" + +/*Disk PUP::er's*/ +void PUP::toDisk::bytes( + void *p, size_t n, size_t itemSize, + dataType /*t*/) { /* CkPrintf("writing %d bytes\n",itemSize*n); */ + if (CmiFwrite(p, itemSize, n, F) != n) { + error = true; + } +} + +void PUP::toDisk::pup_buffer(void *&p, size_t n, size_t itemSize, dataType t) { + bytes(p, n, itemSize, t); + if (isDeleting()) + free(p); +} + +void PUP::toDisk::pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate) { + bytes(p, n, itemSize, t); + if (isDeleting()) + deallocate(p); +} + +void PUP::fromDisk::bytes( + void *p, size_t n, size_t itemSize, + dataType /*t*/) { /* CkPrintf("reading %d bytes\n",itemSize*n); */ + CmiFread(p, itemSize, n, F); +} + +void PUP::fromDisk::pup_buffer(void *&p, size_t n, size_t itemSize, + dataType t) { + if (isUnpacking()) + p = malloc(n * itemSize); + bytes(p, n, itemSize, t); +} + +void PUP::fromDisk::pup_buffer(void *&p, size_t n, size_t itemSize, dataType t, + std::function allocate, + std::function deallocate) { + if (isUnpacking()) + p = allocate(n * itemSize); + bytes(p, n, itemSize, t); +} + +/****************** Seek support ******************* +For seeking: +Occasionally, one will need to pack and unpack items in different +orders (e.g., pack the user data, then the runtime support; but +unpack the runtime support first, then the user data). These routines +support this, via the "PUP::seekBlock" class. + +The abstraction is a (nestable) "seek block", which may contain +several "seek sections". A typical use is: +//Code: + PUP::seekBlock s(p,2); + if (p.isUnpacking()) {s.seek(0); rt.pack(p); } + s.seek(1); ud.pack(p); + if (p.isPacking()) {s.seek(0); rt.pack(p); } + s.endBlock(); +*/ +PUP::seekBlock::seekBlock(PUP::er &Np, int nSections) : nSec(nSections), p(Np) { + if (nSections < 0 || nSections > maxSections) + CmiAbort("Invalid # of sections passed to PUP::seekBlock!"); + p.impl_startSeek(*this); + if (p.isPacking()) { // Must fabricate the section table + secTabOff = p.impl_tell(*this); + for (int i = 0; i <= nSec; i++) + secTab[i] = -1; + } + p(secTab, nSec + 1); + hasEnded = false; +} +PUP::seekBlock::~seekBlock() { + if (!hasEnded) + endBlock(); +} + +void PUP::seekBlock::seek(int toSection) { + if (toSection < 0 || toSection >= nSec) + CmiAbort("Invalid section # passed to PUP::seekBlock::seek!"); + if (p.isPacking()) // Build the section table + secTab[toSection] = p.impl_tell(*this); + else if (p.isUnpacking()) // Extract the section table + p.impl_seek(*this, secTab[toSection]); + /*else ignore the seeking*/ +} + +void PUP::seekBlock::endBlock(void) { + if (p.isPacking()) { + // Finish off and write out the section table + secTab[nSec] = p.impl_tell(*this); + p.impl_seek(*this, secTabOff); + p(secTab, nSec + 1); // Write out the section table + } + // Seek to the end of the seek block + p.impl_seek(*this, secTab[nSec]); + p.impl_endSeek(*this); + hasEnded = true; +} + +/** PUP::er seek implementation routines **/ +/*Default seek implementations are empty, which is the +appropriate behavior for, e.g., sizers. +*/ +void PUP::er::impl_startSeek(PUP::seekBlock &s) /*Begin a seeking block*/ +{} +size_t PUP::er::impl_tell(seekBlock &s) /*Give the current offset*/ +{ + return 0; +} +void PUP::er::impl_seek(seekBlock &s, size_t off) /*Seek to the given offset*/ +{} +void PUP::er::impl_endSeek(seekBlock &s) /*End a seeking block*/ +{} + +/*Memory buffer seeking is trivial*/ +void PUP::mem::impl_startSeek(seekBlock &s) /*Begin a seeking block*/ +{ + s.data.ptr = buf; +} +size_t PUP::mem::impl_tell(seekBlock &s) /*Give the current offset*/ +{ + return buf - s.data.ptr; +} +void PUP::mem::impl_seek(seekBlock &s, size_t off) /*Seek to the given offset*/ +{ + buf = s.data.ptr + off; +} + +/*Disk buffer seeking is also simple*/ +void PUP::disk::impl_startSeek(seekBlock &s) /*Begin a seeking block*/ +{ + s.data.loff = ftell(F); +} +size_t PUP::disk::impl_tell(seekBlock &s) /*Give the current offset*/ +{ + return (int)(ftell(F) - s.data.loff); +} +void PUP::disk::impl_seek(seekBlock &s, size_t off) /*Seek to the given offset*/ +{ + fseek(F, s.data.loff + off, 0); +} + +/*PUP::wrap_er just forwards seek calls to its wrapped PUP::er.*/ +void PUP::wrap_er::impl_startSeek(seekBlock &s) /*Begin a seeking block*/ +{ + p.impl_startSeek(s); +} +size_t PUP::wrap_er::impl_tell(seekBlock &s) /*Give the current offset*/ +{ + return p.impl_tell(s); +} +void PUP::wrap_er::impl_seek(seekBlock &s, + size_t off) /*Seek to the given offset*/ +{ + p.impl_seek(s, off); +} +void PUP::wrap_er::impl_endSeek(seekBlock &s) /*Finish a seeking block*/ +{ + p.impl_endSeek(s); +} + +/**************** PUP::able support ********************** +If a class C inherits from PUP::able, +and you keep a new/delete pointer to C "C *cptr" somewhere, +you can call "p(cptr)" in your pup routine, and the object +will be saved/delete'd/new'd/restored properly with no +additional effort, even if C has virtual methods or is actually +a subclass of C. There is no space or time overhead for C +objects other than the virtual function. + +This is implemented by registering a constructor and ID +for each PUP::able class. A packer can then write the ID +before the class; and unpacker can look up the constructor +from the ID. + */ + +static PUP::able::PUP_ID null_PUP_ID(0); /*ID of null object*/ + +PUP::able *PUP::able::clone(void) const { + // Make a new object to fill out + PUP::able *ret = get_constructor(get_PUP_ID())(); + + // Save our own state into a buffer + PUP::able *mthis = (PUP::able *)this; /* cast away constness */ + size_t size; + { + PUP::sizer ps; + mthis->pup(ps); + size = ps.size(); + } + void *buf = malloc(size); + { + PUP::toMem pt(buf); + mthis->pup(pt); + } + + // Fill the new object with our values + { + PUP::fromMem pf(buf); + ret->pup(pf); + } + free(buf); + + return ret; +} + +// Empty destructor & pup routine +PUP::able::~able() {} +void PUP::able::pup(PUP::er &p) {} + +// Compute a good hash of the given string +// (registration-time only-- allowed to be slow) +void PUP::able::PUP_ID::setName(const char *name) { + int i, o, n = strlen(name); + unsigned int t[len] = {0}; + for (o = 0; o < n; o++) + for (i = 0; i < len; i++) { + unsigned char c = name[o]; + unsigned int shift1 = (((o + 2) * (i + 1) * 5 + 4) % 13); + unsigned int shift2 = (((o + 2) * (i + 1) * 3 + 2) % 11) + 13; + t[i] += (c << shift1) + (c << shift2); + } + for (i = 0; i < len; i++) + hash[i] = (unsigned char)(t[i] % 20117 + t[i] % 1217 + t[i] % 157); +} + +// Registration routines-- called at global initialization time +class PUP_regEntry { +public: + PUP::able::PUP_ID id; + const char *name; + PUP::able::constructor_function ctor; + PUP_regEntry() : name(nullptr), ctor(nullptr) {} // Default constructor + PUP_regEntry(const char *Nname, const PUP::able::PUP_ID &Nid, + PUP::able::constructor_function Nctor) + : id(Nid), name(Nname), ctor(Nctor) {} + PUP_regEntry(int zero) { + name = NULL; // For marking "not found" + } +}; + +typedef std::map PUP_registry; + +// FIXME: not SMP safe! // gzheng +static PUP_registry *PUP_getRegistry(void) { + static PUP_registry *reg = NULL; + if (reg == NULL) + reg = new PUP_registry(); + return reg; +} + +const PUP_regEntry *PUP_getRegEntry(const PUP::able::PUP_ID &id, + const char *const name_hint = NULL) { + auto it = PUP_getRegistry()->find(id); + const PUP_regEntry *cur = + (it != PUP_getRegistry()->end()) ? &it->second : NULL; + if (cur == NULL) { + if (name_hint != NULL) + CmiAbort("Unrecognized PUP::able::PUP_ID for %s", name_hint); + CmiAbort( + "Unrecognized PUP::able::PUP_ID. is there an unregistered module?"); + } + return cur; +} + +PUP::able::PUP_ID PUP::able::register_constructor(const char *className, + constructor_function fn) { + PUP::able::PUP_ID id(className); + (*PUP_getRegistry())[id] = PUP_regEntry(className, id, fn); + return id; +} + +PUP::able::constructor_function +PUP::able::get_constructor(const PUP::able::PUP_ID &id) { + return PUP_getRegEntry(id)->ctor; +} + +// For allocatable objects: new/delete object and call pup routine +void PUP::er::object(able **a) { + const PUP_regEntry *r = NULL; + if (isUnpacking()) { // Find the object type & create the object + PUP::able::PUP_ID id; // The object's id + id.pup(*this); + if (id == null_PUP_ID) { + *a = NULL; + return; + } + r = PUP_getRegEntry(id); + // Invoke constructor (calls new) + *a = (r->ctor)(); + + } else { // Just write out the object type + if (*a == NULL) { + null_PUP_ID.pup(*this); + return; + } else { + const PUP::able::PUP_ID &id = (*a)->get_PUP_ID(); + id.pup(*this); + r = PUP_getRegEntry(id, typeid(**a).name()); + } + } + syncComment(PUP::sync_begin_object, r->name); + (*a)->pup(*this); + syncComment(PUP::sync_end_object); +} + +/****************** Text Pup ******************/ + +char *PUP::toTextUtil::beginLine(void) { + // Indent level tabs over: + for (int i = 0; i < level; i++) + cur[i] = '\t'; + cur[level] = 0; + return cur + level; +} +void PUP::toTextUtil::endLine(void) { cur = advance(cur); } +void PUP::toTextUtil::beginEnv(const char *type, int n) { + char *const begin = beginLine(); + char *o = begin; + snprintf(o, maxCount, "begin "); + o += strlen(o); + snprintf(o, maxCount - (o - begin), type, n); + o += strlen(o); + snprintf(o, maxCount - (o - begin), " {\n"); + endLine(); + level++; +} +void PUP::toTextUtil::endEnv(const char *type) { + level--; + snprintf(beginLine(), maxCount, "} end %s;\n", type); + endLine(); +} +PUP::toTextUtil::toTextUtil(unsigned int inType, char *buf, size_t len) + : er(inType) { + cur = buf; + maxCount = len; + level = 0; +} + +void PUP::toTextUtil::comment(const char *message) { + snprintf(beginLine(), maxCount, "//%s\n", message); + endLine(); +} + +void PUP::toTextUtil::synchronize(unsigned int m) { + snprintf(beginLine(), maxCount, "sync=0x%08x\n", m); + endLine(); +#if 0 /* text people don't care this much about synchronization */ + char *o=beginLine(); + sprintf(o,"sync=");o+=strlen(o); + const char *consonants="bcdfgjklmprstvxz"; + const char *vowels="aeou"; + for (int firstBit=0;firstBit<32;firstBit+=6) { + sprintf(o,"%c%c%c", consonants[0xf&(m>>firstBit)], + vowels[0x3&(m>>(firstBit+4))], + (firstBit==30)?';':'-'); + o+=strlen(o); + } + sprintf(o,"\n"); endLine(); +#endif +} + +void PUP::toTextUtil::pup_buffer(void *&p, size_t n, size_t itemSize, + dataType t) { + bytes(p, n, itemSize, t); +} + +void PUP::toTextUtil::pup_buffer(void *&p, size_t n, size_t itemSize, + dataType t, + std::function allocate, + std::function deallocate) { + bytes(p, n, itemSize, t); +} + +void PUP::toTextUtil::bytes(void *p, size_t n, size_t itemSize, dataType t) { + if (t == Tchar) { /*Character data is written out directly (rather than + numerically)*/ + char *const begin = beginLine(); + char *o = begin; + snprintf(o, maxCount, "string="); + o += strlen(o); + *o++ = '\"'; /*Leading quote*/ + /*Copy each character, possibly escaped*/ + const char *c = (const char *)p; + for (size_t i = 0; i < n; i++) { + if (c[i] == '\n') { + snprintf(o, maxCount - (o - begin), "\\n"); + o += strlen(o); + } else if (iscntrl(c[i])) { + snprintf(o, maxCount - (o - begin), "\\x%02X", (unsigned char)c[i]); + o += strlen(o); + } else if (c[i] == '\\' || c[i] == '\"') { + snprintf(o, maxCount - (o - begin), "\\%c", c[i]); + o += strlen(o); + } else + *o++ = c[i]; + } + /*Add trailing quote and newline*/ + snprintf(o, maxCount - (o - begin), "\";\n"); + o += strlen(o); + endLine(); + } else if (t == Tbyte || t == Tuchar) { /*Byte data is written out in hex + (rather than decimal) */ + beginEnv("byte %d", n); + const unsigned char *c = (const unsigned char *)p; + char *const begin = beginLine(); + char *o = begin; + for (size_t i = 0; i < n; i++) { + snprintf(o, maxCount - (o - begin), "%02X ", c[i]); + o += strlen(o); + if (i % 25 == 24 && (i + 1 != n)) { /* This line is too long-- wrap it */ + snprintf(o, maxCount - (o - begin), "\n"); + o += strlen(o); + endLine(); + o = beginLine(); + } + } + snprintf(o, maxCount - (o - begin), "\n"); + endLine(); + endEnv("byte"); + } else { /*Ordinary number-- write out in decimal */ + if (n != 1) + beginEnv("array %d", n); + for (size_t i = 0; i < n; i++) { + char *const o = beginLine(); + switch (t) { + case Tshort: + snprintf(o, maxCount, "short=%d;\n", ((short *)p)[i]); + break; + case Tushort: + snprintf(o, maxCount, "ushort=%u;\n", ((unsigned short *)p)[i]); + break; + case Tint: + snprintf(o, maxCount, "int=%d;\n", ((int *)p)[i]); + break; + case Tuint: + snprintf(o, maxCount, "uint=%u;\n", ((unsigned int *)p)[i]); + break; + case Tlong: + snprintf(o, maxCount, "long=%ld;\n", ((long *)p)[i]); + break; + case Tulong: + snprintf(o, maxCount, "ulong=%lu;\n", ((unsigned long *)p)[i]); + break; + case Tfloat: + snprintf(o, maxCount, "float=%.7g;\n", ((float *)p)[i]); + break; + case Tdouble: + snprintf(o, maxCount, "double=%.15g;\n", ((double *)p)[i]); + break; + case Tbool: + snprintf(o, maxCount, "bool=%s;\n", ((bool *)p)[i] ? "true" : "false"); + break; +#if CMK_LONG_DOUBLE_DEFINED + case Tlongdouble: + snprintf(o, maxCount, "longdouble=%Lg;\n", ((long double *)p)[i]); + break; +#endif +#ifdef CMK_PUP_LONG_LONG + case Tlonglong: + snprintf(o, maxCount, "longlong=%lld;\n", ((CMK_PUP_LONG_LONG *)p)[i]); + break; + case Tulonglong: + snprintf(o, maxCount, "ulonglong=%llu;\n", + ((unsigned CMK_PUP_LONG_LONG *)p)[i]); + break; +#endif + case Tpointer: + snprintf(o, maxCount, "pointer=%p;\n", ((void **)p)[i]); + break; + default: + CmiAbort("Unrecognized pup type code!"); + } + endLine(); + } + if (n != 1) + endEnv("array"); + } +} +void PUP::toTextUtil::object(able **a) { + beginEnv("object"); + er::object(a); + endEnv("object"); +} + +// Text sizer +char *PUP::sizerText::advance(char *cur) { + charCount += strlen(cur); + return line; +} + +PUP::sizerText::sizerText(void) + : toTextUtil(IS_SIZING + IS_COMMENTS, line, sizerText::lineLen), + charCount(0) {} + +// Text packer +char *PUP::toText::advance(char *cur) { + charCount += strlen(cur); + return buf + charCount; +} + +PUP::toText::toText(char *outBuf, size_t len) + : toTextUtil(IS_PACKING + IS_COMMENTS, outBuf, len), buf(outBuf), + charCount(0) {} + +/************** To/from text FILE ****************/ +void PUP::toTextFile::pup_buffer(void *&p, size_t n, size_t itemSize, + dataType t) { + bytes(p, n, itemSize, t); +} + +void PUP::toTextFile::pup_buffer(void *&p, size_t n, size_t itemSize, + dataType t, + std::function allocate, + std::function deallocate) { + bytes(p, n, itemSize, t); +} + +void PUP::toTextFile::bytes(void *p, size_t n, size_t itemSize, dataType t) { + for (size_t i = 0; i < n; i++) + switch (t) { + case Tchar: + fprintf(f, " '%c'", ((char *)p)[i]); + break; + case Tuchar: + case Tbyte: + fprintf(f, " %02X", ((unsigned char *)p)[i]); + break; + case Tshort: + fprintf(f, " %d", ((short *)p)[i]); + break; + case Tushort: + fprintf(f, " %u", ((unsigned short *)p)[i]); + break; + case Tint: + fprintf(f, " %d", ((int *)p)[i]); + break; + case Tuint: + fprintf(f, " %u", ((unsigned int *)p)[i]); + break; + case Tlong: + fprintf(f, " %ld", ((long *)p)[i]); + break; + case Tulong: + fprintf(f, " %lu", ((unsigned long *)p)[i]); + break; + case Tfloat: + fprintf(f, " %.7g", ((float *)p)[i]); + break; + case Tdouble: + fprintf(f, " %.15g", ((double *)p)[i]); + break; + case Tbool: + fprintf(f, " %s", ((bool *)p)[i] ? "true" : "false"); + break; +#if CMK_LONG_DOUBLE_DEFINED + case Tlongdouble: + fprintf(f, " %Lg", ((long double *)p)[i]); + break; +#endif +#ifdef CMK_PUP_LONG_LONG + case Tlonglong: + fprintf(f, " %lld", ((CMK_PUP_LONG_LONG *)p)[i]); + break; + case Tulonglong: + fprintf(f, " %llu", ((unsigned CMK_PUP_LONG_LONG *)p)[i]); + break; +#endif + case Tpointer: + fprintf(f, " %p", ((void **)p)[i]); + break; + default: + CmiAbort("Unrecognized pup type code!"); + }; + fprintf(f, "\n"); +} +void PUP::toTextFile::comment(const char *message) { + fprintf(f, "! %s\n", message); +} + +void PUP::fromTextFile::parseError(const char *what) { + // find line number by counting how many returns + long cur = ftell(f); + int lineno = 0; + rewind(f); + while (!feof(f)) { + char c; + if (fscanf(f, "%c", &c) != 1) { + CmiAbort("PUP> reading text from file failed!"); + } + if (c == '\n') + lineno++; + if (ftell(f) > cur) + break; + } + fprintf(stderr, "Parse error during pup from text file: %s at line: %d\n", + what, lineno); + CmiAbort("Parse error during pup from text file!\n"); +} +int PUP::fromTextFile::readInt(const char *fmt) { + int ret = 0; + if (1 != fscanf(f, fmt, &ret)) { + if (feof(f)) + return 0; /* start spitting out zeros at EOF */ + else + parseError("could not match integer"); + } + return ret; +} +unsigned int PUP::fromTextFile::readUint(const char *fmt) { + unsigned int ret = 0; + if (1 != fscanf(f, fmt, &ret)) { + if (feof(f)) + return 0u; /* start spitting out zeros at EOF */ + else + parseError("could not match unsigned integer"); + } + return ret; +} +CMK_TYPEDEF_INT8 PUP::fromTextFile::readLongInt(const char *fmt) { + CMK_TYPEDEF_INT8 ret = 0; + if (1 != fscanf(f, fmt, &ret)) { + if (feof(f)) + return 0u; + else + parseError("could not match large integer"); + } + return ret; +} +double PUP::fromTextFile::readDouble(void) { + double ret = 0; + if (1 != fscanf(f, "%lg", &ret)) { + if (feof(f)) + return 0.0; /* start spitting out zeros at EOF */ + else + parseError("could not match double"); + } + return ret; +} + +void PUP::fromTextFile::pup_buffer(void *&p, size_t n, size_t itemSize, + dataType t) { + bytes(p, n, itemSize, t); +} + +void PUP::fromTextFile::pup_buffer(void *&p, size_t n, size_t itemSize, + dataType t, + std::function allocate, + std::function deallocate) { + bytes(p, n, itemSize, t); +} + +void PUP::fromTextFile::bytes(void *p, size_t n, size_t itemSize, dataType t) { + for (size_t i = 0; i < n; i++) + switch (t) { + case Tchar: + if (1 != fscanf(f, " '%c'", &((char *)p)[i])) + parseError("Could not match character"); + break; + case Tuchar: + case Tbyte: + ((unsigned char *)p)[i] = (unsigned char)readInt("%02X"); + break; + case Tshort: + ((short *)p)[i] = (short)readInt(); + break; + case Tushort: + ((unsigned short *)p)[i] = (unsigned short)readUint(); + break; + case Tint: + ((int *)p)[i] = readInt(); + break; + case Tuint: + ((unsigned int *)p)[i] = readUint(); + break; + case Tlong: + ((long *)p)[i] = readInt(); + break; + case Tulong: + ((unsigned long *)p)[i] = readUint(); + break; + case Tfloat: + ((float *)p)[i] = (float)readDouble(); + break; + case Tdouble: + ((double *)p)[i] = readDouble(); + break; +#if CMK_LONG_DOUBLE_DEFINED + case Tlongdouble: { + long double ret = 0; + if (1 != fscanf(f, "%Lg", &ret)) + parseError("could not match long double"); + ((long double *)p)[i] = ret; + } break; +#endif +#ifdef CMK_PUP_LONG_LONG + case Tlonglong: { + CMK_PUP_LONG_LONG ret = 0; + if (1 != fscanf(f, "%lld", &ret)) + parseError("could not match long long"); + ((CMK_PUP_LONG_LONG *)p)[i] = ret; + } break; + case Tulonglong: { + unsigned CMK_PUP_LONG_LONG ret = 0; + if (1 != fscanf(f, "%llu", &ret)) + parseError("could not match unsigned long long"); + ((unsigned CMK_PUP_LONG_LONG *)p)[i] = ret; + } break; +#endif + case Tbool: { + char tmp[20]; + if (1 != fscanf(f, " %19s", tmp)) + parseError("could not read boolean string"); + bool val = false; + if (0 == strcmp(tmp, "true")) + val = true; + else if (0 == strcmp(tmp, "false")) + val = false; + else + parseError("could not recognize boolean string"); + ((bool *)p)[i] = val; + } break; + case Tpointer: { + void *ret = 0; + if (1 != fscanf(f, "%p", &ret)) + parseError("could not match pointer"); + ((void **)p)[i] = ret; + } break; + default: + CmiAbort("Unrecognized pup type code!"); + }; +} +void PUP::fromTextFile::comment(const char *message) { + char c; + // Skip to the start of the message: + while (isspace(c = fgetc(f))) { + } + + if (c != '!') + return; // This isn't the start of a comment + // Skip over the whole line containing the comment: + char *commentBuf = (char *)CmiTmpAlloc(1024); + if (fgets(commentBuf, 1024, f) == NULL) { + CmiAbort("PUP> skipping over comment in text file failed!"); + } + CmiTmpFree(commentBuf); +} diff --git a/src/pup/pup_xlater.C b/src/pup/pup_xlater.C new file mode 100644 index 0000000..998445e --- /dev/null +++ b/src/pup/pup_xlater.C @@ -0,0 +1,324 @@ +/* +Pack/UnPack Library for UIUC Parallel Programming Lab +Orion Sky Lawlor, olawlor@uiuc.edu, 4/5/2000 + +This part of the pack/unpack library handles translating +between different binary representations for integers and floats. +All machines are assumed to be byte-oriented. + +Currently supported are converting between 8,16,32,64, and 128-bit +integers, and swapping bytes between big and little integers and +big and little-endian IEEE 32- and 64-bit floats. + +*/ +#include +#include +#include +#include "pup.h" + +//////////////////// MachineInfo utilities /////////////////// + +//This 4-byte sequence identifies a PPL machineInfo structure. +static const unsigned char machInfo_magic[4]={0x10,0xea,0xbd,0xf9}; + +//Return true if our magic number is valid. +bool PUP::machineInfo::valid(void) const +{ + for (int i=0;i<4;i++) + if (magic[i]!=machInfo_magic[i]) + return false; + return true; +} + +//Return true if we differ from the current (running) machine. +bool PUP::machineInfo::needsConversion(void) const +{ + const machineInfo &m=current(); + if (intFormat==m.intFormat && floatFormat==m.floatFormat && + intBytes[0]==m.intBytes[0] && intBytes[1]==m.intBytes[1] && + intBytes[2]==m.intBytes[2] && intBytes[3]==m.intBytes[3] && + floatBytes==m.floatBytes && doubleBytes==m.doubleBytes && + boolBytes==m.boolBytes && pointerBytes==m.pointerBytes + ) + return false;//No conversion needed + else + return true;//Some differences-- convert +} + +////////// For getting info. about the current machine ///////// +static int getIntFormat(void) +{ + int test=0x1c; + unsigned char *c=(unsigned char *)&test; + if (c[sizeof(int)-1]==0x1c) + //Macintosh and most workstations are big-endian + return 0;//Big-endian machine + if (c[0]==0x1c) + //Intel x86 PC's, and DEC VAX are little-endian + return 1;//Little-endian machine + return 99;//Unknown integer type +} +/*Known values for this routine come from this (compressed) program: +main() {double d=-9.5; unsigned char *c=(unsigned char *)&d; +int i; for (i=0;imagic[i]=machInfo_magic[i]; + m->version=1; + m->intBytes[0]=sizeof(char); + m->intBytes[1]=sizeof(short); + m->intBytes[2]=sizeof(int); + m->intBytes[3]=sizeof(long); +#if CMK_HAS_INT16 + m->intBytes[4]=sizeof(CmiInt16); +#else + m->intBytes[4]=0; +#endif + m->intFormat=getIntFormat(); + m->floatBytes=sizeof(float); + m->doubleBytes=sizeof(double); + m->floatFormat=getFloatFormat(); + m->boolBytes=sizeof(bool); + m->pointerBytes=sizeof(void*); + //m->padding[0]=0; // version 1 does not have padding field + } + return *m; +} + +////////////////////////// Conversion Functions /////////////////////////// +typedef unsigned char myByte; + +//Do nothing to the given bytes (the "null conversion") +static void cvt_null(int N,const myByte *in,myByte *out,size_t nElem) {} + +//Swap the order of each N-byte chunk in the given array (in can equal out) +static void cvt_swap(int N,const myByte *in,myByte *out,size_t nElem) +{ + size_t i; + for (i=0;i=0;j--) + {t=s[j];d[j]=s[N-j-1];d[N-j-1]=t;} + } +} +/******************************************************* +Convert N-byte boolean to machine boolean. +*/ +static void cvt_bool(int N,const myByte *in,myByte *out,size_t nElem) +{ + size_t i; + for (i=nElem;i>1;i--) + { + const myByte *s=&in[N*(i-1)]; + bool ret=false; + int j; + for (j=0;j1) abort();//Unknown integer format + //Set up table for converting integral types + setConverterInt(src,cur,0,0,Tchar); + setConverterInt(src,cur,0,1,Tshort); + setConverterInt(src,cur,0,2,Tint); + setConverterInt(src,cur,0,3,Tlong); + setConverterInt(src,cur,1,0,Tuchar); + setConverterInt(src,cur,1,1,Tushort); + setConverterInt(src,cur,1,2,Tuint); + setConverterInt(src,cur,1,3,Tulong); + if (src.intFormat==cur.intFormat) //At worst, need to swap 8-byte integers: + convertFn[Tlonglong]=convertFn[Tulonglong]=cvt_null; + else + convertFn[Tlonglong]=convertFn[Tulonglong]=cvt_swap; +#if CMK_HAS_INT16 + setConverterInt(src,cur,0,4,Tint128); + setConverterInt(src,cur,1,4,Tuint128); +#endif + convertFn[Tfloat]=converterFloat(src,cur,src.floatBytes,cur.floatBytes); + convertFn[Tdouble]=converterFloat(src,cur,src.doubleBytes,cur.doubleBytes); + convertFn[Tlongdouble]=cvt_null; //<- a lie, but no better alternative + + if (src.boolBytes!=cur.boolBytes) + convertFn[Tbool]=cvt_bool; + else + convertFn[Tbool]=cvt_null; + + convertFn[Tbyte]=cvt_null;//Bytes are never converted at all + setConverterInt(src,cur,0,2,Tsync); + convertFn[Tpointer]=cvt_null; //<- a lie, but pointers should never be converted across machines + + //Finish out the size table (integer portion is done by setConverterInt) +#ifdef CMK_PUP_LONG_LONG + convertSize[Tlonglong]=convertSize[Tlonglong]=sizeof(CMK_PUP_LONG_LONG); +#else + convertSize[Tlonglong]=convertSize[Tlonglong]=8; +#endif + convertSize[Tfloat]=src.floatBytes; + convertSize[Tdouble]=src.doubleBytes; +#if CMK_LONG_DOUBLE_DEFINED + convertSize[Tlongdouble]=sizeof(long double); +#else + convertSize[Tlongdouble]=12; //<- again, a lie. Need machineInfo.longdoubleSize! +#endif + convertSize[Tbool]=src.boolBytes; + convertSize[Tbyte]=1;//Byte always takes one byte of storage + convertSize[Tpointer]=src.pointerBytes; +} + +void PUP::xlater::pup_buffer(void *&ptr,size_t n,size_t itemSize,dataType t) { + bytes(ptr, n, itemSize, t); +} + +void PUP::xlater::pup_buffer(void *&ptr,size_t n, size_t itemSize, dataType t, std::function allocate, std::function deallocate) { + bytes(ptr, n, itemSize, t); +} + +//Generic bottleneck: unpack n items of size itemSize from p. +void PUP::xlater::bytes(void *ptr,size_t n,size_t itemSize,dataType t) +{ + if (convertSize[t]==itemSize) + {//Do conversion in-place + p.bytes(ptr,n,itemSize,t); + convertFn[t](itemSize,(const myByte *)ptr,(myByte *)ptr,n);//Convert in-place + } + else + {//Read into temporary buffer, unpack, and then convert + void *buf=(void *)malloc(convertSize[t]*n); + p.bytes(buf,n,convertSize[t],t); + convertFn[t](convertSize[t],(const myByte *)buf,(myByte *)ptr,n); + free(buf); + } +} + diff --git a/src/pup/pupf.h b/src/pup/pupf.h new file mode 100644 index 0000000..2dcc2e2 --- /dev/null +++ b/src/pup/pupf.h @@ -0,0 +1,52 @@ + external fpup_int + external fpup_ints + external fpup_char + external fpup_chars + external fpup_short + external fpup_shorts + external fpup_real + external fpup_reals + external fpup_double + external fpup_doubles + external fpup_logical + external fpup_logicals + interface + function fpup_issizing(p) + INTEGER :: p + logical fpup_issizing + end function + function fpup_ispacking(p) + INTEGER :: p + logical fpup_ispacking + end function + function fpup_isunpacking(p) + INTEGER :: p + logical fpup_isunpacking + end function + function fpup_isdeleting(p) + INTEGER :: p + logical fpup_isdeleting + end function + function fpup_isuserlevel(p) + INTEGER :: p + logical fpup_isuserlevel + end function + subroutine fpup_complex(p,c) + INTEGER p + complex c + end subroutine + subroutine fpup_complexes(p,c,size) + INTEGER p + complex,pointer,dimension(:) :: c + INTEGER size + end subroutine + subroutine fpup_doublecomplex(p,c) + INTEGER p + double complex c + end subroutine + subroutine fpup_doublecomplexes(p,c,size) + INTEGER p + double complex,pointer,dimension(:) :: c + INTEGER size + end subroutine + end interface diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index aee3fd4..0cd1e10 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(reduction_node) add_subdirectory(cth) add_subdirectory(self_send) add_subdirectory(ctv) +add_subdirectory(pup_test) # Add a test that configures, builds, and runs standalone_project add_test(NAME standalone_project_build_and_test diff --git a/tests/pup_test/CMakeLists.txt b/tests/pup_test/CMakeLists.txt new file mode 100644 index 0000000..0b93728 --- /dev/null +++ b/tests/pup_test/CMakeLists.txt @@ -0,0 +1,2 @@ +add_reconverse_executable(pup_test pup_test.cpp) +add_test(NAME pup_test COMMAND pup_test +pe 1) diff --git a/tests/pup_test/pup_test.cpp b/tests/pup_test/pup_test.cpp new file mode 100644 index 0000000..ebc8796 --- /dev/null +++ b/tests/pup_test/pup_test.cpp @@ -0,0 +1,237 @@ +#include "conv-rdma.h" +#include "converse.h" +#include "pup.h" +#include +#include + +// Message structure for Converse +struct Message { + CmiMessageHeader header; +}; + +// Test class that demonstrates PUP serialization +class TestData { +public: + int intValue; + double doubleValue; + char stringValue[256]; + int intArray[10]; + int arraySize; + CmiNcpyBuffer ncpyBuffer; + + TestData() : intValue(0), doubleValue(0.0), arraySize(0) { + stringValue[0] = '\0'; + memset(intArray, 0, sizeof(intArray)); + } + + TestData(int i, double d, const char *s, int *arr, int arrSize) + : intValue(i), doubleValue(d), arraySize(arrSize) { + strncpy(stringValue, s, sizeof(stringValue) - 1); + stringValue[sizeof(stringValue) - 1] = '\0'; + + for (int j = 0; j < arrSize && j < 10; j++) { + intArray[j] = arr[j]; + } + + // Initialize ncpyBuffer with some test data + char *testData = (char *)malloc(100); + for (int j = 0; j < 100; j++) { + testData[j] = 'A' + (j % 26); + } + ncpyBuffer = CmiNcpyBuffer(testData, 100); + } + + void pup(PUP::er &p) { + p(intValue); + p(doubleValue); + p(stringValue, 256); // Serialize as char array + p(arraySize); // Serialize array size first + p(intArray, arraySize); + // Note: CmiNcpyBuffer has its own pup method, so we can use it directly + ncpyBuffer.pup(p); + } + + void print() const { + printf("TestData: int=%d, double=%.2f, string='%s', array=[", intValue, + doubleValue, stringValue); + for (int i = 0; i < arraySize; i++) { + printf("%d", intArray[i]); + if (i < arraySize - 1) + printf(", "); + } + printf("], ncpyBuffer.ptr=%p, ncpyBuffer.cnt=%zu\n", ncpyBuffer.ptr, + ncpyBuffer.cnt); + } +}; + +// Simple test class without PUPable inheritance +class SimpleTestClass { +public: + int value; + char name[256]; + + SimpleTestClass() : value(0) { name[0] = '\0'; } + + SimpleTestClass(int v, const char *n) : value(v) { + strncpy(name, n, sizeof(name) - 1); + name[sizeof(name) - 1] = '\0'; + } + + void pup(PUP::er &p) { + p(value); + p(name, 256); // Serialize as char array + } + + void print() const { + printf("SimpleTestClass: value=%d, name='%s'\n", value, name); + } +}; + +CpvDeclare(int, testHandlerId); +CpvDeclare(int, exitHandlerId); + +void test_handler(void *vmsg) { + printf("=== PUP Test Handler Called ===\n"); + + // Test 1: Basic PUP serialization with TestData + printf("\n--- Test 1: Basic PUP Serialization ---\n"); + int testArray[] = {1, 2, 3, 4, 5}; + TestData original(42, 3.14159, "Hello PUP!", testArray, 5); + printf("Original data:\n"); + original.print(); + + // Size the data + PUP::sizer sizer; + sizer | original; + size_t dataSize = sizer.size(); + printf("Serialized size: %zu bytes\n", dataSize); + + // Pack the data + char *buffer = (char *)malloc(dataSize); + PUP::toMem packer(buffer); + packer | original; + printf("Packed %zu bytes\n", packer.size()); + + // Unpack the data + TestData restored; + PUP::fromMem unpacker(buffer); + unpacker | restored; + printf("Restored data:\n"); + restored.print(); + + // Verify data integrity + bool dataMatch = (original.intValue == restored.intValue) && + (original.doubleValue == restored.doubleValue) && + (strcmp(original.stringValue, restored.stringValue) == 0) && + (original.arraySize == restored.arraySize); + + // Check array contents + for (int i = 0; i < original.arraySize && dataMatch; i++) { + if (original.intArray[i] != restored.intArray[i]) { + dataMatch = false; + } + } + + printf("Data integrity check: %s\n", dataMatch ? "PASSED" : "FAILED"); + + // Test 2: Simple class serialization + printf("\n--- Test 2: Simple Class Serialization ---\n"); + SimpleTestClass originalSimple(100, "TestObject"); + printf("Original Simple:\n"); + originalSimple.print(); + + // Size the simple object + PUP::sizer sizer2; + sizer2 | originalSimple; + size_t simpleSize = sizer2.size(); + printf("Simple serialized size: %zu bytes\n", simpleSize); + + // Pack the simple object + char *buffer2 = (char *)malloc(simpleSize); + PUP::toMem packer2(buffer2); + packer2 | originalSimple; + printf("Packed Simple %zu bytes\n", packer2.size()); + + // Unpack the simple object + SimpleTestClass restoredSimple; + PUP::fromMem unpacker2(buffer2); + unpacker2 | restoredSimple; + printf("Restored Simple:\n"); + restoredSimple.print(); + + // Verify simple object integrity + bool simpleMatch = (originalSimple.value == restoredSimple.value) && + (strcmp(originalSimple.name, restoredSimple.name) == 0); + printf("Simple object integrity check: %s\n", + simpleMatch ? "PASSED" : "FAILED"); + + // Test 3: Network byte order serialization + printf("\n--- Test 3: Network Byte Order Serialization ---\n"); + int networkArray[] = {0xDEAD, 0xBEEF, 0xCAFE}; + TestData networkTest(0x12345678, 2.71828, "Network Test", networkArray, 3); + printf("Original network test data:\n"); + networkTest.print(); + + // Size for network serialization + PUP::sizer networkSizer; + networkSizer | networkTest; + size_t networkSize = networkSizer.size(); + printf("Network serialized size: %zu bytes\n", networkSize); + + // Pack to network byte order + char *networkBuffer = (char *)malloc(networkSize); + PUP::toMem networkPacker(networkBuffer); + networkPacker | networkTest; + printf("Packed to network format %zu bytes\n", networkPacker.size()); + + // Unpack from network byte order + TestData networkRestored; + PUP::fromMem networkUnpacker(networkBuffer); + networkUnpacker | networkRestored; + printf("Restored from network format:\n"); + networkRestored.print(); + + // Cleanup + free(buffer); + free(buffer2); + free(networkBuffer); + + printf("\n=== All PUP Tests Completed ===\n"); + + // Send exit message + Message *exitMsg = new Message; + exitMsg->header.handlerId = CpvAccess(exitHandlerId); + exitMsg->header.messageSize = sizeof(Message); + CmiSyncSendAndFree(0, exitMsg->header.messageSize, exitMsg); +} + +void exit_handler(void *vmsg) { + printf("PUP test completed successfully!\n"); + CsdExitScheduler(); +} + +CmiStartFn mymain(int argc, char **argv) { + printf("Starting PUP Test Program on PE %d\n", CmiMyRank()); + + // Register handlers + CpvInitialize(int, testHandlerId); + CpvAccess(testHandlerId) = CmiRegisterHandler(test_handler); + + CpvInitialize(int, exitHandlerId); + CpvAccess(exitHandlerId) = CmiRegisterHandler(exit_handler); + + if (CmiMyRank() == 0) { + // Create and send test message + Message *msg = new Message; + msg->header.handlerId = CpvAccess(testHandlerId); + msg->header.messageSize = sizeof(Message); + CmiSyncSendAndFree(0, msg->header.messageSize, msg); + } + + return 0; +} + +int main(int argc, char **argv) { + ConverseInit(argc, argv, (CmiStartFn)mymain); + return 0; +}