diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index 4516950e0..f435d870b 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -29,13 +30,6 @@ #define epicsExportSharedSymbols #include "chfPlugin.h" -#ifndef HUGE_VALF -# define HUGE_VALF HUGE_VAL -#endif -#ifndef HUGE_VALL -# define HUGE_VALL (-(HUGE_VAL)) -#endif - /* * Data for a chfPlugin */ @@ -65,10 +59,11 @@ typedef enum chfPluginType { } chfPluginType; /* - * Convert the (long) integer value 'val' to the type named in 'opt->optType' - * and store the result at 'user + opt->offset'. + * Convert the (epicsInt32) integer value 'val' to the type named in + * 'opt->optType' and store the result at 'user + opt->offset'. */ -static int store_integer_value(const chfPluginArgDef *opt, void *user, long val) +static int +store_integer_value(const chfPluginArgDef *opt, char *user, epicsInt32 val) { epicsInt32 *ival; int *eval; @@ -77,7 +72,10 @@ static int store_integer_value(const chfPluginArgDef *opt, void *user, long val) char *sval; char buff[22]; /* 2^64 = 1.8e+19, so 20 digits plus sign max */ -/* printf("Got an integer for %s (type %d): %ld\n", opt->name, opt->optType, val); */ +#ifdef DEBUG_CHF + printf("Got an integer for %s (type %d): %ld\n", + opt->name, opt->optType, (long) val); +#endif if (!opt->convert && opt->optType != chfPluginArgInt32) { return -1; @@ -89,24 +87,23 @@ static int store_integer_value(const chfPluginArgDef *opt, void *user, long val) *ival = val; break; case chfPluginArgBoolean: - eval = (int*) ((char*)user + opt->offset); - *eval = !!val; + sval = user + opt->offset; + *sval = !!val; break; case chfPluginArgDouble: - dval = (double*) ((char*)user + opt->offset); - *dval = (double) val; + dval = (double*) (user + opt->offset); + *dval = val; break; case chfPluginArgString: - sval = ((char*)user + opt->offset); - sprintf(buff, "%ld", val); - if (strlen(buff) > opt->size-1) { + sval = user + opt->offset; + if (sprintf(buff, "%ld", (long)val) > opt->size - 1) { return -1; } strncpy(sval, buff, opt->size-1); sval[opt->size-1]='\0'; break; case chfPluginArgEnum: - eval = (int*) ((char*)user + opt->offset); + eval = (int*) (user + opt->offset); for (emap = opt->enums; emap && emap->name; emap++) { if (val == emap->value) { *eval = val; @@ -127,14 +124,16 @@ static int store_integer_value(const chfPluginArgDef *opt, void *user, long val) * Convert the (int) boolean value 'val' to the type named in 'opt->optType' * and store the result at 'user + opt->offset'. */ -static int store_boolean_value(const chfPluginArgDef *opt, void *user, int val) +static int store_boolean_value(const chfPluginArgDef *opt, char *user, int val) { epicsInt32 *ival; - int *eval; double *dval; char *sval; -/* printf("Got a boolean for %s (type %d): %d\n", opt->name, opt->optType, val); */ +#ifdef DEBUG_CHF + printf("Got a boolean for %s (type %d): %d\n", + opt->name, opt->optType, val); +#endif if (!opt->convert && opt->optType != chfPluginArgBoolean) { return -1; @@ -142,24 +141,24 @@ static int store_boolean_value(const chfPluginArgDef *opt, void *user, int val) switch (opt->optType) { case chfPluginArgInt32: - ival = (epicsInt32 *) ((char*)user + opt->offset); + ival = (epicsInt32 *) (user + opt->offset); *ival = val; break; case chfPluginArgBoolean: - eval = (int*) ((char*)user + opt->offset); - *eval = val; + sval = user + opt->offset; + *sval = val; break; case chfPluginArgDouble: - dval = (double*) ((char*)user + opt->offset); - *dval = val ? 1. : 0.; + dval = (double*) (user + opt->offset); + *dval = !!val; break; case chfPluginArgString: - sval = ((char*)user + opt->offset); - if ((val ? 4 : 5) > opt->size-1) { + sval = user + opt->offset; + if ((val ? 4 : 5) > opt->size - 1) { return -1; } - strncpy(sval, (val ? "true" : "false"), opt->size-1); - sval[opt->size-1]='\0'; + strncpy(sval, val ? "true" : "false", opt->size - 1); + sval[opt->size - 1] = '\0'; break; case chfPluginArgEnum: case chfPluginArgInvalid: @@ -172,43 +171,42 @@ static int store_boolean_value(const chfPluginArgDef *opt, void *user, int val) * Convert the double value 'val' to the type named in 'opt->optType' * and store the result at 'user + opt->offset'. */ -static int store_double_value(const chfPluginArgDef *opt, void *user, double val) +static int +store_double_value(const chfPluginArgDef *opt, void *user, double val) { epicsInt32 *ival; - int *eval; double *dval; char *sval; int i; -/* +#ifdef DEBUG_CHF printf("Got a double for %s (type %d, convert: %s): %g\n", - opt->name, opt->optType, opt->convert?"yes":"no", val); -*/ + opt->name, opt->optType, opt->convert ? "yes" : "no", val); +#endif + if (!opt->convert && opt->optType != chfPluginArgDouble) { return -1; } switch (opt->optType) { case chfPluginArgInt32: -#if (LONG_MAX > 0x7fffffff) - if (val < -0x80000000L || val > 0x7fffffff) { + if (val < INT_MIN || val > INT_MAX) { return -1; } -#endif - ival = (epicsInt32 *) ((char*)user + opt->offset); - *ival = val; + ival = (epicsInt32 *) (user + opt->offset); + *ival = (epicsInt32) val; break; case chfPluginArgBoolean: - eval = (int*) ((char*)user + opt->offset); - *eval = (val != 0.) ? 1 : 0; + sval = user + opt->offset; + *sval = !!val; break; case chfPluginArgDouble: - dval = (double*) ((char*)user + opt->offset); + dval = (double*) (user + opt->offset); *dval = val; break; case chfPluginArgString: - sval = ((char*)user + opt->offset); - if (opt->size <= 8) { /* Play it safe: 3 exp + 2 sign + 'e' + '.' */ + sval = user + opt->offset; + if (opt->size <= 8) { /* Play it safe: 3 exp + 2 sign + 'e' + '.' */ return -1; } i = epicsSnprintf(sval, opt->size, "%.*g", (int) opt->size - 7, val); @@ -227,10 +225,11 @@ static int store_double_value(const chfPluginArgDef *opt, void *user, double val * Convert the (char*) string value 'val' to the type named in 'opt->optType' * and store the result at 'user + opt->offset'. */ -static int store_string_value(const chfPluginArgDef *opt, void *user, const char *val, size_t len) +static int +store_string_value(const chfPluginArgDef *opt, char *user, const char *val, + size_t len) { epicsInt32 *ival; - long lval; int *eval; const chfPluginEnumType *emap; double *dval; @@ -238,57 +237,47 @@ static int store_string_value(const chfPluginArgDef *opt, void *user, const char char *end; int i; -/* printf("Got a string for %s (type %d): %.*s\n", opt->name, opt->optType, len, val); */ +#ifdef DEBUG_CHF + printf("Got a string for %s (type %d): %.*s\n", + opt->name, opt->optType, (int) len, val); +#endif - if (!opt->convert && opt->optType != chfPluginArgString && opt->optType != chfPluginArgEnum) { + if (!opt->convert && opt->optType != chfPluginArgString && + opt->optType != chfPluginArgEnum) { return -1; } switch (opt->optType) { case chfPluginArgInt32: - lval = strtol(val, &end, 0); - /* test for the myriad error conditions which strtol may use */ - if (lval == LONG_MAX || lval == LONG_MIN -#if (LONG_MAX > 0x7fffffff) - || lval < -0x80000000L || lval > 0x7fffffff -#endif - || end == val) { - return -1; - } - ival = (epicsInt32 *) ((char*)user + opt->offset); - *ival = lval; - break; + ival = (epicsInt32 *) (user + opt->offset); + return epicsParseInt32(val, ival, 0, &end); + case chfPluginArgBoolean: - eval = (int*) ((char*)user + opt->offset); + sval = user + opt->offset; if (epicsStrnCaseCmp(val, "true", len) == 0) { - *eval = 1; + *sval = 1; } else if (epicsStrnCaseCmp(val, "false", len) == 0) { - *eval = 0; + *sval = 0; } else { - i = strtol(val, &end, 0); - if (i > INT_MAX || i < INT_MIN || end == val) { + epicsInt8 i8; + + if (epicsParseInt8(val, &i8, 0, &end)) return -1; - } - *eval = !!i; + *sval = !!i8; } break; case chfPluginArgDouble: - dval = (double*) ((char*)user + opt->offset); - *dval = strtod(val, &end); - /* Indicates errors in the same manner as strtol */ - if (*dval==HUGE_VALF||*dval==HUGE_VALL||end==val ) - { - return -1; - } - break; + dval = (double*) (user + opt->offset); + return epicsParseDouble(val, dval, &end); + case chfPluginArgString: i = opt->size-1 < len ? opt->size-1 : len; - sval = ((char*)user + opt->offset); + sval = user + opt->offset; strncpy(sval, val, i); sval[i] = '\0'; break; case chfPluginArgEnum: - eval = (int*) ((char*)user + opt->offset); + eval = (int*) (user + opt->offset); for (emap = opt->enums; emap && emap->name; emap++) { if (strncmp(emap->name, val, len) == 0) { *eval = emap->value; @@ -327,7 +316,7 @@ static parse_result parse_start(chFilter *filter) /* FIXME: Use a free-list */ f = calloc(1, sizeof(chfFilter)); if (!f) { - fprintf(stderr,"chfFilterCtx calloc failed\n"); + errlogPrintf("chfFilterCtx calloc failed\n"); goto errfctx; } f->nextParam = -1; @@ -336,7 +325,7 @@ static parse_result parse_start(chFilter *filter) /* FIXME: Use a free-list */ f->found = calloc( (p->nopts/32)+1, sizeof(epicsUInt32) ); if (!f->found) { - fprintf(stderr,"chfConfigParseStart: bit array calloc failed\n"); + errlogPrintf("chfConfigParseStart: bit array calloc failed\n"); goto errbitarray; } @@ -401,7 +390,8 @@ static parse_result parse_boolean(chFilter *filter, int boolVal) const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; chfFilter *f = (chfFilter*)filter->puser; - if (f->nextParam < 0 || store_boolean_value(&opts[f->nextParam], f->puser, boolVal)) { + if (f->nextParam < 0 || + store_boolean_value(&opts[f->nextParam], f->puser, boolVal)) { return parse_stop; } else { return parse_continue; @@ -413,7 +403,13 @@ static parse_result parse_integer(chFilter *filter, long integerVal) const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; chfFilter *f = (chfFilter*)filter->puser; - if (f->nextParam < 0 || store_integer_value(&opts[f->nextParam], f->puser, integerVal)) { + if(sizeof(long)>sizeof(epicsInt32)) { + epicsInt32 temp=integerVal; + if(integerVal !=temp) + return parse_stop; + } + if (f->nextParam < 0 || + store_integer_value(&opts[f->nextParam], f->puser, integerVal)) { return parse_stop; } else { return parse_continue; @@ -425,19 +421,22 @@ static parse_result parse_double(chFilter *filter, double doubleVal) const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; chfFilter *f = (chfFilter*)filter->puser; - if (f->nextParam < 0 || store_double_value(&opts[f->nextParam], f->puser, doubleVal)) { + if (f->nextParam < 0 || + store_double_value(&opts[f->nextParam], f->puser, doubleVal)) { return parse_stop; } else { return parse_continue; } } -static parse_result parse_string(chFilter *filter, const char *stringVal, size_t stringLen) +static parse_result +parse_string(chFilter *filter, const char *stringVal, size_t stringLen) { const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; chfFilter *f = (chfFilter*)filter->puser; - if (f->nextParam < 0 || store_string_value(&opts[f->nextParam], f->puser, stringVal, stringLen)) { + if (f->nextParam < 0 || + store_string_value(&opts[f->nextParam], f->puser, stringVal, stringLen)) { return parse_stop; } else { return parse_continue; @@ -449,7 +448,8 @@ static parse_result parse_start_map(chFilter *filter) return parse_continue; } -static parse_result parse_map_key(chFilter *filter, const char *key, size_t stringLen) +static parse_result +parse_map_key(chFilter *filter, const char *key, size_t stringLen) { const chfPluginArgDef *cur; const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; @@ -481,31 +481,38 @@ static long channel_open(chFilter *filter) chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; - if (p->pif->channel_open) return p->pif->channel_open(filter->chan, f->puser); - else return 0; + if (p->pif->channel_open) + return p->pif->channel_open(filter->chan, f->puser); + else + return 0; } -static void channel_register_pre(chFilter *filter, - chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +static void +channel_register_pre(chFilter *filter, chPostEventFunc **cb_out, + void **arg_out, db_field_log *probe) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; if (p->pif->channelRegisterPre) - p->pif->channelRegisterPre(filter->chan, f->puser, cb_out, arg_out, probe); + p->pif->channelRegisterPre(filter->chan, f->puser, cb_out, arg_out, + probe); } -static void channel_register_post(chFilter *filter, - chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +static void +channel_register_post(chFilter *filter, chPostEventFunc **cb_out, + void **arg_out, db_field_log *probe) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; if (p->pif->channelRegisterPost) - p->pif->channelRegisterPost(filter->chan, f->puser, cb_out, arg_out, probe); + p->pif->channelRegisterPost(filter->chan, f->puser, cb_out, arg_out, + probe); } -static void channel_report(chFilter *filter, int level, const unsigned short indent) +static void channel_report(chFilter *filter, int level, + const unsigned short indent) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; @@ -525,11 +532,20 @@ static void channel_close(chFilter *filter) free(f); /* FIXME: Use a free-list */ } +static void plugin_free(void* puser) +{ + chfPlugin *p=puser; + free(p->required); + free(p); +} + /* * chFilterIf for the wrapper * we just support a simple one-level map, and no arrays */ static chFilterIf wrapper_fif = { + plugin_free, + parse_start, parse_abort, parse_end, @@ -554,7 +570,8 @@ static chFilterIf wrapper_fif = { channel_close }; -const char* chfPluginEnumString(const chfPluginEnumType *emap, int i, const char* def) +const char* +chfPluginEnumString(const chfPluginEnumType *emap, int i, const char* def) { for(; emap && emap->name; emap++) { if ( i == emap->value ) { @@ -564,7 +581,9 @@ const char* chfPluginEnumString(const chfPluginEnumType *emap, int i, const char return def; } -int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts) +int +chfPluginRegister(const char* key, const chfPluginIf *pif, + const chfPluginArgDef* opts) { chfPlugin *p; size_t i; @@ -576,25 +595,22 @@ int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginAr switch(cur->optType) { case chfPluginArgInt32: if (cur->size < sizeof(epicsInt32)) { - errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for epicsInt32 (%lu)\n", - key, cur->size, cur->name, - (unsigned long) sizeof(epicsInt32)); + errlogPrintf("Plugin %s: %d bytes too small for epicsInt32 %s\n", + key, cur->size, cur->name); return -1; } break; case chfPluginArgBoolean: if (cur->size < 1) { - errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for boolean (%lu)\n", - key, cur->size, cur->name, - (unsigned long) sizeof(char)); + errlogPrintf("Plugin %s: %d bytes too small for boolean %s\n", + key, cur->size, cur->name); return -1; } break; case chfPluginArgDouble: if (cur->size < sizeof(double)) { - errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for double (%lu)\n", - key, cur->size, cur->name, - (unsigned long) sizeof(double)); + errlogPrintf("Plugin %s: %d bytes too small for double %s\n", + key, cur->size, cur->name); return -1; } break; @@ -603,17 +619,15 @@ int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginAr /* Catch if someone has given us a char* instead of a char[] * Also means that char buffers must be >=4. */ - errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for string (>= %lu)\n", - key, cur->size, cur->name, - (unsigned long) sizeof(char*)); + errlogPrintf("Plugin %s: %d bytes too small for string %s\n", + key, cur->size, cur->name); return -1; } break; case chfPluginArgEnum: if (cur->size < sizeof(int)) { - errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for enum (%lu)\n", - key, cur->size, cur->name, - (unsigned long) sizeof(int)); + errlogPrintf("Plugin %s: %d bytes too small for enum %s\n", + key, cur->size, cur->name); return -1; } break; @@ -628,7 +642,7 @@ int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginAr /* Bit array used to find missing required keys */ reqd = dbCalloc((i/32)+1, sizeof(epicsUInt32)); if (!reqd) { - fprintf(stderr,"Plugin %s: bit array calloc failed\n", key); + errlogPrintf("Plugin %s: bit array calloc failed\n", key); return -1; } diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h index 7d9745d10..99739aab8 100644 --- a/src/ioc/db/chfPlugin.h +++ b/src/ioc/db/chfPlugin.h @@ -44,11 +44,12 @@ struct db_field_log; * * typedef struct myStruct { * ... other stuff - * epicsUInt32 ival; + * epicsInt32 ival; * double dval; - * epicsUInt32 ival2; + * epicsInt32 ival2; * int enumval; * char strval[20]; + * char boolval; * } myStruct; * * static const @@ -61,6 +62,7 @@ struct db_field_log; * chfDouble(myStruct, dval, "Double" , 1, 0), * chfString(myStruct, strval , "String" , 1, 0), * chfEnum (myStruct, enumval, "Color" , 1, 0, colorEnum), + * chfBoolean(myStruct, boolval, "Bool" , 1, 0), * chfPluginEnd * }; * diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 9305f7da4..d27527b37 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -584,6 +584,8 @@ long dbChannelOpen(dbChannel *chan) } /* Set up type probe */ + probe.type = dbfl_type_val; + probe.ctx = dbfl_context_read; probe.field_type = dbChannelFieldType(chan); probe.no_elements = dbChannelElements(chan); probe.field_size = dbChannelFieldSize(chan); diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 2e50185aa..f646cac9d 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -72,6 +72,10 @@ typedef enum { /* These routines must be implemented by each filter plug-in */ typedef struct chFilterIf { + /* cleanup pointer passed to dbRegisterFilter(). + * Called during DB shutdown + */ + void (* priv_free)(void *puser); /* Parsing event handlers: */ parse_result (* parse_start)(chFilter *filter); /* If parse_start() returns parse_continue for a filter, one of diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 6fbb9a03a..24d81a04a 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -74,6 +74,7 @@ struct event_user { epicsMutexId lock; epicsEventId ppendsem; /* Wait while empty */ epicsEventId pflush_sem; /* wait for flush */ + epicsEventId pexitsem; /* wait for event task to join */ EXTRALABORFUNC *extralabor_sub;/* off load to event task */ void *extralabor_arg;/* parameter to above */ @@ -276,35 +277,44 @@ dbEventCtx epicsShareAPI db_init_events (void) return NULL; } + /* Flag will be cleared when event task starts */ + evUser->pendexit = TRUE; + evUser->firstque.evUser = evUser; evUser->firstque.writelock = epicsMutexCreate(); - if (!evUser->firstque.writelock) { - return NULL; - } + if (!evUser->firstque.writelock) + goto fail; evUser->ppendsem = epicsEventCreate(epicsEventEmpty); - if (!evUser->ppendsem) { - epicsMutexDestroy (evUser->firstque.writelock); - return NULL; - } + if (!evUser->ppendsem) + goto fail; evUser->pflush_sem = epicsEventCreate(epicsEventEmpty); - if (!evUser->pflush_sem) { - epicsMutexDestroy (evUser->firstque.writelock); - epicsEventDestroy (evUser->ppendsem); - return NULL; - } + if (!evUser->pflush_sem) + goto fail; evUser->lock = epicsMutexCreate(); - if (!evUser->lock) { - epicsMutexDestroy (evUser->firstque.writelock); - epicsEventDestroy (evUser->pflush_sem); - epicsEventDestroy (evUser->ppendsem); - return NULL; - } + if (!evUser->lock) + goto fail; + evUser->pexitsem = epicsEventCreate(epicsEventEmpty); + if (!evUser->pexitsem) + goto fail; evUser->flowCtrlMode = FALSE; evUser->extraLaborBusy = FALSE; evUser->pSuicideEvent = NULL; return (dbEventCtx) evUser; +fail: + if(evUser->lock) + epicsMutexDestroy (evUser->lock); + if(evUser->firstque.writelock) + epicsMutexDestroy (evUser->firstque.writelock); + if(evUser->ppendsem) + epicsEventDestroy (evUser->ppendsem); + if(evUser->pflush_sem) + epicsEventDestroy (evUser->pflush_sem); + if(evUser->pexitsem) + epicsEventDestroy (evUser->pexitsem); + freeListFree(dbevEventUserFreeList,evUser); + return NULL; } /* @@ -328,10 +338,26 @@ void epicsShareAPI db_close_events (dbEventCtx ctx) * hazardous to the system's health. */ epicsMutexMustLock ( evUser->lock ); - evUser->pendexit = TRUE; + if(!evUser->pendexit) { /* event task running */ + evUser->pendexit = TRUE; + epicsMutexUnlock ( evUser->lock ); + + /* notify the waiting task */ + epicsEventSignal(evUser->ppendsem); + /* wait for task to exit */ + epicsEventMustWait(evUser->pexitsem); + + epicsMutexMustLock ( evUser->lock ); + } + epicsMutexUnlock ( evUser->lock ); - /* notify the waiting task */ - epicsEventSignal(evUser->ppendsem); + + epicsEventDestroy(evUser->pexitsem); + epicsEventDestroy(evUser->ppendsem); + epicsEventDestroy(evUser->pflush_sem); + epicsMutexDestroy(evUser->lock); + + freeListFree(dbevEventUserFreeList, evUser); } /* @@ -824,7 +850,7 @@ void epicsShareAPI db_post_single_event (dbEventSubscription event) pLog = db_create_event_log(pevent); pLog = dbChannelRunPreChain(pevent->chan, pLog); - db_queue_event_log(pevent, pLog); + if(pLog) db_queue_event_log(pevent, pLog); dbScanUnlock (prec); } @@ -1006,14 +1032,10 @@ static void event_task (void *pParm) } } - epicsEventDestroy(evUser->ppendsem); - epicsEventDestroy(evUser->pflush_sem); - epicsMutexDestroy(evUser->lock); - - freeListFree(dbevEventUserFreeList, evUser); - taskwdRemove(epicsThreadGetIdSelf()); + epicsEventSignal(evUser->pexitsem); + return; } @@ -1037,7 +1059,6 @@ int epicsShareAPI db_start_events ( return DB_EVENT_OK; } - evUser->pendexit = FALSE; evUser->init_func = init_func; evUser->init_func_arg = init_func_arg; if (!taskname) { @@ -1051,6 +1072,7 @@ int epicsShareAPI db_start_events ( epicsMutexUnlock ( evUser->lock ); return DB_EVENT_ERROR; } + evUser->pendexit = FALSE; epicsMutexUnlock ( evUser->lock ); return DB_EVENT_OK; } diff --git a/src/ioc/db/db_field_log.h b/src/ioc/db/db_field_log.h index 1f1d212d0..0a503744f 100644 --- a/src/ioc/db/db_field_log.h +++ b/src/ioc/db/db_field_log.h @@ -73,6 +73,13 @@ struct dbfl_val { union native_value field; /* Field value */ }; +/* External data reference. + * If dtor is provided then it should be called when the referenced + * data is no longer needed. This is done automatically by + * db_delete_field_log(). Any code which changes a dbfl_type_ref + * field log to another type, or to reference different data, + * must explicitly call the dtor function. + */ struct dbfl_ref { dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */ void *pvt; /* Private pointer */ @@ -81,7 +88,9 @@ struct dbfl_ref { typedef struct db_field_log { enum dbfl_type type:2; /* type (union) selector */ + /* ctx is used for all types */ enum dbfl_context ctx:1; /* context (operation type) */ + /* the following are used for value and reference types */ epicsTimeStamp time; /* Time stamp */ unsigned short stat; /* Alarm Status */ unsigned short sevr; /* Alarm Severity */ @@ -94,6 +103,29 @@ typedef struct db_field_log { } u; } db_field_log; +/* + * A db_field_log will in one of three types: + * + * dbfl_type_rec - Reference to record + * The field log stores no data itself. Data must instead be taken + * via the dbChannel* which must always be provided when along + * with the field log. + * For this type only the 'type' and 'ctx' members are used. + * + * dbfl_type_ref - Reference to outside value + * Used for variable size (array) data types. Meta-data + * is stored in the field log, but value data is stored externally + * (see struct dbfl_ref). + * For this type all meta-data members are used. The dbfl_ref side of the + * data union is used. + * + * dbfl_type_val - Internal value + * Used to store small scalar data. Meta-data and value are + * present in this structure and no external references are used. + * For this type all meta-data members are used. The dbfl_val side of the + * data union is used. + */ + #ifdef __cplusplus } #endif diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 86f4853a1..a2cc93192 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -1,5 +1,5 @@ #************************************************************************* -# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. @@ -10,48 +10,63 @@ TOP=../../../.. include $(TOP)/configure/CONFIG -PROD_vxWorks += dbTestHarness -PROD_RTEMS += dbTestHarness - PROD_LIBS = dbCore ca Com TESTPROD_HOST += callbackTest callbackTest_SRCS += callbackTest.c -dbTestHarness_SRCS += callbackTest.c +testHarness_SRCS += callbackTest.c TESTS += callbackTest - -dbTestHarness_SRCS += epicsRunDbTests.c -dbTestHarness_SRCS_RTEMS = rtemsTestHarness.c - -TESTSPEC_vxWorks = dbTestHarness.munch; epicsRunDbTests -TESTSPEC_RTEMS = dbTestHarness.boot; epicsRunDbTests - -TESTPROD_HOST += dbChannelTest -dbChannelTest_SRCS += dbChannelTest.c xRecord_registerRecordDeviceDriver.cpp -OBJS_IOC_vxWorks += dbChannelTest -TESTS += dbChannelTest - -TESTPROD_HOST += chfPluginTest -chfPluginTest_SRCS += chfPluginTest.c xRecord_registerRecordDeviceDriver.cpp -OBJS_IOC_vxWorks += chfPluginTest -TESTS += chfPluginTest - -TESTPROD_HOST += arrShorthandTest -arrShorthandTest_SRCS += arrShorthandTest.c xRecord_registerRecordDeviceDriver.cpp -OBJS_IOC_vxWorks += arrShorthandTest -TESTS += arrShorthandTest - TESTPROD_HOST += dbStateTest dbStateTest_SRCS += dbStateTest.c -OBJS_IOC_vxWorks += dbStateTest +testHarness_SRCS += dbStateTest.c TESTS += dbStateTest +TARGETS += $(COMMON_DIR)/dbChannelTest.dbd +dbChannelTest_DBD += xRecord.dbd +TESTPROD_HOST += dbChannelTest +dbChannelTest_SRCS += xRecord.c +dbChannelTest_SRCS += dbChannelTest.c +dbChannelTest_SRCS += dbChannelTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += dbChannelTest.c +testHarness_SRCS += dbChannelTest_registerRecordDeviceDriver.cpp +TESTS += dbChannelTest + +TARGETS += $(COMMON_DIR)/chfPluginTest.dbd +chfPluginTest_DBD += xRecord.dbd +TESTPROD_HOST += chfPluginTest +chfPluginTest_SRCS += xRecord.c +chfPluginTest_SRCS += chfPluginTest.c +chfPluginTest_SRCS += chfPluginTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += chfPluginTest.c +testHarness_SRCS += chfPluginTest_registerRecordDeviceDriver.cpp +TESTS += chfPluginTest + +TARGETS += $(COMMON_DIR)/arrShorthandTest.dbd +arrShorthandTest_DBD += xRecord.dbd +TESTPROD_HOST += arrShorthandTest +arrShorthandTest_SRCS += xRecord.c +arrShorthandTest_SRCS += arrShorthandTest.c +arrShorthandTest_SRCS += arrShorthandTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += arrShorthandTest.c +testHarness_SRCS += arrShorthandTest_registerRecordDeviceDriver.cpp +TESTS += arrShorthandTest + +# The testHarness runs all the test programs in a known working order. +testHarness_SRCS += epicsRunDbTests.c +testHarness_SRCS += xRecord.c + +dbTestHarness_SRCS += $(testHarness_SRCS) +dbTestHarness_SRCS_RTEMS += rtemsTestHarness.c + +PROD_vxWorks = dbTestHarness +PROD_RTEMS = dbTestHarness + +TESTSPEC_vxWorks = dbTestHarness.munch; epicsRunDbTests +TESTSPEC_RTEMS = dbTestHarness.boot; epicsRunDbTests + TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES -dbChannelTest$(DEP): $(COMMON_DIR)/xRecord.h -chfPluginTest$(DEP): $(COMMON_DIR)/xRecord.h -arrShorthandTest$(DEP): $(COMMON_DIR)/xRecord.h -dbStateTest$(DEP): $(COMMON_DIR)/xRecord.h +xRecord$(DEP): $(COMMON_DIR)/xRecord.h diff --git a/src/ioc/db/test/arrShorthandTest.c b/src/ioc/db/test/arrShorthandTest.c index 669eab9ca..2b72c077d 100644 --- a/src/ioc/db/test/arrShorthandTest.c +++ b/src/ioc/db/test/arrShorthandTest.c @@ -21,8 +21,10 @@ #include "chfPlugin.h" #include "dbStaticLib.h" #include "dbAccessDefs.h" +#include "registry.h" #include "epicsUnitTest.h" #include "testMain.h" +#include "osiFileName.h" typedef struct myStruct { epicsInt32 start; @@ -75,9 +77,9 @@ static void testHead (char* title) { testDiag("--------------------------------------------------------"); } -void xRecord_registerRecordDeviceDriver(struct dbBase *); +void arrShorthandTest_registerRecordDeviceDriver(struct dbBase *); -MAIN(chfPluginTest) +MAIN(arrShorthandTest) { dbChannel *pch; @@ -85,11 +87,14 @@ MAIN(chfPluginTest) db_init_events(); - if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + if (dbReadDatabase(&pdbbase, "arrShorthandTest.dbd", + "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR + "../O.Common", NULL)) testAbort("Database description not loaded"); - xRecord_registerRecordDeviceDriver(pdbbase); - if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + arrShorthandTest_registerRecordDeviceDriver(pdbbase); + if (dbReadDatabase(&pdbbase, "xRecord.db", + "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Test database not loaded"); testHead("Register plugin"); @@ -124,16 +129,8 @@ MAIN(chfPluginTest) TESTGOOD("range with incr [s:i:e]", "[2:3:4]", 2, 3, 4); dbFreeBase(pdbbase); + registryFree(); + pdbbase = NULL; return testDone(); } - - -#define GEN_SIZE_OFFSET -#include "xRecord.h" - -#include -#include - -static rset xRSET; -epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 20db71830..a46f33d42 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -15,8 +15,10 @@ #include "chfPlugin.h" #include "dbStaticLib.h" #include "dbAccessDefs.h" +#include "registry.h" #include "epicsUnitTest.h" #include "testMain.h" +#include "osiFileName.h" #define PATTERN 0x55555555 #define TYPE_START 0xAAA @@ -445,15 +447,22 @@ static chfPluginIf postPif = { static int checkValues(myStruct *my, epicsUInt32 i, int f, double d, char *s, int c) { + int ret = 1; if (!my) return 0; - if (my->sent1 == PATTERN && my->sent2 == PATTERN && my->sent3 == PATTERN - && my->sent4 == PATTERN && my->sent5 == PATTERN && my->sent6 == PATTERN - && my->ival == i && my->flag == f && my->dval == d && my->enumval == c - && (strcmp(s, my->str) == 0)) { - return 1; - } else { - return 0; - } +#define CHK(A,B,FMT) if((A)!=(B)) {testDiag("Fail: " #A " (" FMT ") != " #B " (" FMT")", A, B); ret=0;} + CHK(my->sent1, PATTERN, "%08x") + CHK(my->sent2, PATTERN, "%08x") + CHK(my->sent3, PATTERN, "%08x") + CHK(my->sent4, PATTERN, "%08x") + CHK(my->sent5, PATTERN, "%08x") + CHK(my->sent6, PATTERN, "%08x") + CHK(my->ival, i, "%08x") + CHK(my->flag, f, "%02x") + CHK(my->dval, d, "%f") + CHK(my->enumval, c, "%d") +#undef CHK + if(strcmp(s, my->str) != 0) {testDiag("Fail: my->str (%s) != s (%s)", my->str, s); ret=0;} + return ret; } static void testHead (char* title) { @@ -462,7 +471,7 @@ static void testHead (char* title) { testDiag("--------------------------------------------------------"); } -void xRecord_registerRecordDeviceDriver(struct dbBase *); +void chfPluginTest_registerRecordDeviceDriver(struct dbBase *); MAIN(chfPluginTest) { @@ -484,11 +493,14 @@ MAIN(chfPluginTest) testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0, "Enum to string: invalid index"); - if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + if (dbReadDatabase(&pdbbase, "chfPluginTest.dbd", + "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR + "../O.Common", NULL)) testAbort("Database description not loaded"); - xRecord_registerRecordDeviceDriver(pdbbase); - if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + chfPluginTest_registerRecordDeviceDriver(pdbbase); + if (dbReadDatabase(&pdbbase, "xRecord.db", + "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Test database not loaded"); testHead("Try to register buggy plugins"); @@ -818,15 +830,8 @@ MAIN(chfPluginTest) drop = -1; dbFreeBase(pdbbase); + registryFree(); + pdbbase = NULL; return testDone(); } - -#define GEN_SIZE_OFFSET -#include "xRecord.h" - -#include -#include - -static rset xRSET; -epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 94de616f7..2baa5d810 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -16,9 +16,11 @@ #include "dbChannel.h" #include "dbStaticLib.h" #include "dbAccessDefs.h" +#include "registry.h" #include "recSup.h" #include "epicsUnitTest.h" #include "testMain.h" +#include "osiFileName.h" /* Expected call bit definitions */ #define e_start 0x00000001 @@ -143,11 +145,11 @@ void c_close(chFilter *filter) } chFilterIf testIf = - { p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, + { NULL, p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array, c_open, c_reg_pre, c_reg_post, c_report, c_close }; -void xRecord_registerRecordDeviceDriver(struct dbBase *); +void dbChannelTest_registerRecordDeviceDriver(struct dbBase *); MAIN(testDbChannel) /* dbChannelTest is an API routine... */ { @@ -155,11 +157,14 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */ testPlan(66); - if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + if (dbReadDatabase(&pdbbase, "dbChannelTest.dbd", + "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR + "../O.Common", NULL)) testAbort("Database description not loaded"); - xRecord_registerRecordDeviceDriver(pdbbase); - if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + dbChannelTest_registerRecordDeviceDriver(pdbbase); + if (dbReadDatabase(&pdbbase, "xRecord.db", + "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Test database not loaded"); r = e = 0; @@ -247,15 +252,8 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */ testOk1(!dbChannelCreate("x.{\"scalar\":{}}")); dbFreeBase(pdbbase); + registryFree(); + pdbbase = NULL; return testDone(); } - -#define GEN_SIZE_OFFSET -#include "xRecord.h" - -#include -#include - -static rset xRSET; -epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/test/epicsRunDbTests.c b/src/ioc/db/test/epicsRunDbTests.c index 0a3e00cd0..d8633702e 100644 --- a/src/ioc/db/test/epicsRunDbTests.c +++ b/src/ioc/db/test/epicsRunDbTests.c @@ -14,14 +14,25 @@ #include "epicsUnitTest.h" #include "epicsExit.h" +#include "dbmf.h" int callbackTest(void); +int dbStateTest(void); +int testDbChannel(void); +int chfPluginTest(void); +int arrShorthandTest(void); void epicsRunDbTests(void) { testHarness(); runTest(callbackTest); + runTest(dbStateTest); + runTest(testDbChannel); + runTest(chfPluginTest); + runTest(arrShorthandTest); + + dbmfFreeChunks(); epicsExit(0); /* Trigger test harness */ } diff --git a/src/ioc/db/test/rtemsTestHarness.c b/src/ioc/db/test/rtemsTestHarness.c index c9ab2a68c..0aacbe573 100644 --- a/src/ioc/db/test/rtemsTestHarness.c +++ b/src/ioc/db/test/rtemsTestHarness.c @@ -5,10 +5,40 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ + +#ifdef __rtems__ + +#include +#include +#include +#include +#include + +#endif /* __rtems__ */ + extern void epicsRunDbTests(void); int main(int argc, char **argv) { +#ifdef __rtems__ + struct stat s; + printf("Try to create /tmp\n"); + umask(0); + if(mkdir("/tmp", 0777)!=0) + perror("Can't create /tmp"); + if(stat("/tmp", &s)==0) { + printf("Stat /tmp: %o %u,%u\n", s.st_mode, s.st_uid, s.st_gid); + } + epicsEnvSet("TMPDIR","/tmp"); + { + char name[40]; + if(getcwd(name,40)) + printf("Running from %s\n", name); + else + printf("Can't determine PWD"); + } +#endif + epicsRunDbTests(); /* calls epicsExit(0) */ return 0; } diff --git a/src/ioc/db/test/xRecord.c b/src/ioc/db/test/xRecord.c new file mode 100644 index 000000000..568fbb838 --- /dev/null +++ b/src/ioc/db/test/xRecord.c @@ -0,0 +1,25 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Andrew Johnson + * Ralph Lange + */ + +#include "dbAccessDefs.h" +#include + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/test/dbChannelTest.db b/src/ioc/db/test/xRecord.db similarity index 100% rename from src/ioc/db/test/dbChannelTest.db rename to src/ioc/db/test/xRecord.db diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index ce27803a5..f845c73e2 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -31,6 +31,7 @@ #include "epicsStdlib.h" #include "epicsString.h" #include "epicsStdio.h" +#include "dbChannel.h" #define epicsExportSharedSymbols #include "link.h" @@ -592,6 +593,8 @@ void epicsShareAPI dbFreeBase(dbBase *pdbbase) drvSup *pdrvSupNext; brkTable *pbrkTable; brkTable *pbrkTableNext; + chFilterPlugin *pfilt; + chFilterPlugin *pfiltNext; int i; DBENTRY dbentry; @@ -651,6 +654,7 @@ void epicsShareAPI dbFreeBase(dbBase *pdbbase) ellDelete(&pdbRecordType->attributeList,&pAttribute->node); free((void *)pAttribute->name); free((void *)pAttribute->pdbFldDes); + free(pAttribute); pAttribute = pAttributeNext; } pdbRecordTypeNext = (dbRecordType *)ellNext(&pdbRecordType->node); @@ -722,6 +726,15 @@ void epicsShareAPI dbFreeBase(dbBase *pdbbase) free((void *)pbrkTable); pbrkTable = pbrkTableNext; } + pfilt = (chFilterPlugin *)ellFirst(&pdbbase->filterList); + while(pfilt) { + pfiltNext = (chFilterPlugin *)ellNext(&pfilt->node); + free((char*)pfilt->name); + if(pfilt->fif->priv_free) + (*pfilt->fif->priv_free)(pfilt->puser); + free(pfilt); + pfilt = pfiltNext; + } gphFreeMem(pdbbase->pgpHash); dbPvdFreeMem(pdbbase); dbFreePath(pdbbase); diff --git a/src/ioc/registry/registerRecordDeviceDriver.pl b/src/ioc/registry/registerRecordDeviceDriver.pl index 5ab5c14d3..e914473e7 100755 --- a/src/ioc/registry/registerRecordDeviceDriver.pl +++ b/src/ioc/registry/registerRecordDeviceDriver.pl @@ -186,8 +186,7 @@ print << 'END'; } if (executed) { - printf("Registration already done.\n"); - return 0; + printf("Warning: Registration already done.\n"); } executed = 1; diff --git a/src/libCom/dbmf/dbmf.h b/src/libCom/dbmf/dbmf.h index 44395ade3..3740e532e 100644 --- a/src/libCom/dbmf/dbmf.h +++ b/src/libCom/dbmf/dbmf.h @@ -16,6 +16,7 @@ #ifndef DBMF_H #define DBMF_H +#include #include "shareLib.h" #ifdef __cplusplus diff --git a/src/libCom/iocsh/registry.c b/src/libCom/iocsh/registry.c index 89b50ccca..0b2e312e3 100644 --- a/src/libCom/iocsh/registry.c +++ b/src/libCom/iocsh/registry.c @@ -81,6 +81,7 @@ epicsShareFunc void epicsShareAPI registryFree(void) { if(!gphPvt) return; gphFreeMem(gphPvt); + gphPvt = 0; } epicsShareFunc int epicsShareAPI registryDump(void) diff --git a/src/std/filters/dbnd.c b/src/std/filters/dbnd.c index 33e79198d..5056185ee 100644 --- a/src/std/filters/dbnd.c +++ b/src/std/filters/dbnd.c @@ -93,8 +93,10 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { } } } - if (drop) return NULL; - else return pfl; + if (drop) { + db_delete_field_log(pfl); + return NULL; + } else return pfl; } static void channelRegisterPre(dbChannel *chan, void *pvt, diff --git a/src/std/filters/sync.c b/src/std/filters/sync.c index 15c27b267..55ff9303a 100644 --- a/src/std/filters/sync.c +++ b/src/std/filters/sync.c @@ -59,6 +59,8 @@ static void * allocPvt(void) static void freePvt(void *pvt) { + myStruct *my = (myStruct*) pvt; + db_delete_field_log(my->lastfl); freeListFree(myStructFreeList, pvt); } diff --git a/src/std/filters/test/Makefile b/src/std/filters/test/Makefile index 4eb0b1cd6..dffa4e5ee 100644 --- a/src/std/filters/test/Makefile +++ b/src/std/filters/test/Makefile @@ -1,5 +1,5 @@ #************************************************************************* -# Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. @@ -10,30 +10,62 @@ TOP=../../../.. include $(TOP)/configure/CONFIG -PROD_LIBS += dbRecStd dbCore ca Com +PROD_LIBS = dbRecStd dbCore ca Com +TARGETS += $(COMMON_DIR)/tsTest.dbd +tsTest_DBD += xRecord.dbd TESTPROD_HOST += tsTest -tsTest_SRCS += tsTest.c xRecord_registerRecordDeviceDriver.cpp -OBJS_IOC_vxWorks += tsTest +tsTest_SRCS += xRecord.c +tsTest_SRCS += tsTest.c +tsTest_SRCS += tsTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += tsTest.c +testHarness_SRCS += tsTest_registerRecordDeviceDriver.cpp TESTS += tsTest +TARGETS += $(COMMON_DIR)/dbndTest.dbd +dbndTest_DBD += xRecord.dbd TESTPROD_HOST += dbndTest -dbndTest_SRCS += dbndTest.c xRecord_registerRecordDeviceDriver.cpp -OBJS_IOC_vxWorks += dbndTest +dbndTest_SRCS += xRecord.c +dbndTest_SRCS += dbndTest.c +dbndTest_SRCS += dbndTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += dbndTest.c +testHarness_SRCS += dbndTest_registerRecordDeviceDriver.cpp TESTS += dbndTest TARGETS += $(COMMON_DIR)/arrTest.dbd arrTest_DBD += arrRecord.dbd TESTPROD_HOST += arrTest -arrTest_SRCS += arrTest.cpp arrRecord.c arrRecord_registerRecordDeviceDriver.cpp -OBJS_IOC_vxWorks += arrTest +arrTest_SRCS += arrRecord.c +arrTest_SRCS += arrTest.cpp +arrTest_SRCS += arrTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += arrRecord.c +testHarness_SRCS += arrTest.cpp +testHarness_SRCS += arrTest_registerRecordDeviceDriver.cpp TESTS += arrTest +TARGETS += $(COMMON_DIR)/syncTest.dbd +syncTest_DBD += xRecord.dbd TESTPROD_HOST += syncTest -syncTest_SRCS += syncTest.c xRecord_registerRecordDeviceDriver.cpp -OBJS_IOC_vxWorks += syncTest +syncTest_SRCS += xRecord.c +syncTest_SRCS += syncTest.c +syncTest_SRCS += syncTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += syncTest.c +testHarness_SRCS += syncTest_registerRecordDeviceDriver.cpp TESTS += syncTest +# epicsRunFilterTests runs all the test programs in a known working order. +testHarness_SRCS += epicsRunFilterTests.c +testHarness_SRCS += xRecord.c + +filterTestHarness_SRCS += $(testHarness_SRCS) +filterTestHarness_SRCS_RTEMS += rtemsTestHarness.c + +PROD_vxWorks = filterTestHarness +PROD_RTEMS = filterTestHarness + +TESTSPEC_vxWorks = filterTestHarness.munch; epicsRunFilterTests +TESTSPEC_RTEMS = filterTestHarness.boot; epicsRunFilterTests + TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES diff --git a/src/std/filters/test/arrTest.cpp b/src/std/filters/test/arrTest.cpp index 389b1fc9a..c66dfa9c0 100644 --- a/src/std/filters/test/arrTest.cpp +++ b/src/std/filters/test/arrTest.cpp @@ -28,6 +28,8 @@ #include "epicsStdio.h" #include "envDefs.h" #include "dbStaticLib.h" +#include "dbmf.h" +#include "registry.h" #include "subRecord.h" #include "dbAddr.h" #include "dbAccess.h" @@ -37,10 +39,11 @@ #include "dbChannel.h" #include "epicsUnitTest.h" #include "testMain.h" +#include "osiFileName.h" #include "arrRecord.h" -extern "C" int arrRecord_registerRecordDeviceDriver(struct dbBase *pdbbase); +extern "C" int arrTest_registerRecordDeviceDriver(struct dbBase *pdbbase); extern "C" void (*pvar_func_arrInitialize)(void); #define CA_SERVER_PORT "65535" @@ -293,6 +296,19 @@ static void check(short dbr_type) { TEST5B(3, -8, -4, "both sides from-end"); } +static dbEventCtx evtctx; + +static void arrTestCleanup(void* junk) +{ + dbFreeBase(pdbbase); + registryFree(); + pdbbase=0; + + db_close_events(evtctx); + + dbmfFreeChunks(); +} + MAIN(arrTest) { const chFilterPlugin *plug; @@ -304,21 +320,25 @@ MAIN(arrTest) epicsEnvSet("EPICS_CA_SERVER_PORT", server_port); - if (dbReadDatabase(&pdbbase, "arrRecord.dbd", - "..:../../../../../dbd", NULL)) + if (dbReadDatabase(&pdbbase, "arrTest.dbd", + "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR + "../O.Common", NULL)) testAbort("Database description not loaded"); (*pvar_func_arrInitialize)(); - arrRecord_registerRecordDeviceDriver(pdbbase); + arrTest_registerRecordDeviceDriver(pdbbase); registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine); - if (dbReadDatabase(&pdbbase, "arrTest.db", "..", NULL)) + if (dbReadDatabase(&pdbbase, "arrTest.db", + "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Test database not loaded"); + epicsAtExit(&arrTestCleanup,NULL); + /* Start the IOC */ iocInit(); - db_init_events(); + evtctx = db_init_events(); epicsThreadSleep(0.2); testOk(!!(plug = dbFindFilter(arr, strlen(arr))), "plugin arr registered correctly"); @@ -327,11 +347,5 @@ MAIN(arrTest) check(DBR_DOUBLE); check(DBR_STRING); - dbFreeBase(pdbbase); - return testDone(); - - epicsExit(EXIT_SUCCESS); - /*Note that the following statement will never be executed*/ - return 0; } diff --git a/src/std/filters/test/dbndTest.c b/src/std/filters/test/dbndTest.c index 18cccc361..1bc3c009d 100644 --- a/src/std/filters/test/dbndTest.c +++ b/src/std/filters/test/dbndTest.c @@ -16,10 +16,13 @@ #include "dbAccessDefs.h" #include "db_field_log.h" #include "dbCommon.h" +#include "registry.h" #include "chfPlugin.h" #include "epicsUnitTest.h" #include "epicsTime.h" +#include "dbmf.h" #include "testMain.h" +#include "osiFileName.h" #define PATTERN 0x55 @@ -96,7 +99,7 @@ static void testHead (char* title) { testDiag("--------------------------------------------------------"); } -void xRecord_registerRecordDeviceDriver(struct dbBase *); +void dbndTest_registerRecordDeviceDriver(struct dbBase *); MAIN(dbndTest) { @@ -109,18 +112,22 @@ MAIN(dbndTest) void *arg_out = NULL; db_field_log *pfl2; db_field_log fl1; + dbEventCtx evtctx; testPlan(59); - db_init_events(); + evtctx = db_init_events(); - if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + if (dbReadDatabase(&pdbbase, "dbndTest.dbd", + "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR + "../O.Common", NULL)) testAbort("Database description not loaded"); (*pvar_func_dbndInitialize)(); /* manually initialize plugin */ - xRecord_registerRecordDeviceDriver(pdbbase); + dbndTest_registerRecordDeviceDriver(pdbbase); - if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + if (dbReadDatabase(&pdbbase, "xRecord.db", + "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Test database not loaded"); testOk(!!(plug = dbFindFilter(dbnd, strlen(dbnd))), "plugin dbnd registered correctly"); @@ -160,9 +167,13 @@ MAIN(dbndTest) fl_setup(pch, pfl2); mustPassOnce(pch, pfl2, "abs", 0., 0); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + mustPassOnce(pch, pfl2, "abs", 0., 1); - db_delete_field_log(pfl2); dbChannelDelete(pch); /* Delta = -1: pass any update */ @@ -192,11 +203,25 @@ MAIN(dbndTest) fl_setup(pch, pfl2); mustPassOnce(pch, pfl2, "abs", 3., 1); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + mustDrop(pch, pfl2, "abs", 3., 3); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + mustDrop(pch, pfl2, "abs", 3., 4); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + mustPassOnce(pch, pfl2, "abs", 3., 5); - db_delete_field_log(pfl2); dbChannelDelete(pch); /* Delta = relative */ @@ -211,25 +236,51 @@ MAIN(dbndTest) fl_setup(pch, pfl2); mustPassOnce(pch, pfl2, "rel", 50., 1); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + mustPassOnce(pch, pfl2, "rel", 50., 2); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + mustDrop(pch, pfl2, "rel", 50., 3); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + mustPassOnce(pch, pfl2, "rel", 50., 4); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + mustDrop(pch, pfl2, "rel", 50., 5); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + mustDrop(pch, pfl2, "rel", 50., 6); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + mustPassOnce(pch, pfl2, "rel", 50., 7); - db_delete_field_log(pfl2); dbChannelDelete(pch); dbFreeBase(pdbbase); + registryFree(); + pdbbase=0; + + db_close_events(evtctx); + + dbmfFreeChunks(); return testDone(); } - -#define GEN_SIZE_OFFSET -#include "xRecord.h" - -#include -#include - -static rset xRSET; -epicsExportAddress(rset,xRSET); diff --git a/src/std/filters/test/epicsRunFilterTests.c b/src/std/filters/test/epicsRunFilterTests.c new file mode 100644 index 000000000..236364391 --- /dev/null +++ b/src/std/filters/test/epicsRunFilterTests.c @@ -0,0 +1,33 @@ +/*************************************************************************\ +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Run filter tests as a batch. + */ + +#include "epicsUnitTest.h" +#include "epicsExit.h" +#include "dbmf.h" + +int tsTest(void); +int dbndTest(void); +int syncTest(void); +int arrTest(void); + +void epicsRunFilterTests(void) +{ + testHarness(); + + runTest(tsTest); + runTest(dbndTest); + runTest(syncTest); + runTest(arrTest); + + dbmfFreeChunks(); + + epicsExit(0); /* Trigger test harness */ +} diff --git a/src/std/filters/test/syncTest.c b/src/std/filters/test/syncTest.c index 11c772491..8696fa65c 100644 --- a/src/std/filters/test/syncTest.c +++ b/src/std/filters/test/syncTest.c @@ -17,11 +17,14 @@ #include "db_field_log.h" #include "dbCommon.h" #include "dbChannel.h" +#include "registry.h" #include "chfPlugin.h" +#include "dbmf.h" #include "epicsUnitTest.h" #include "epicsTime.h" #include "dbState.h" #include "testMain.h" +#include "osiFileName.h" #define PATTERN 0x55 @@ -127,7 +130,7 @@ static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) { checkCtxRead(pch, red); } -void xRecord_registerRecordDeviceDriver(struct dbBase *); +void syncTest_registerRecordDeviceDriver(struct dbBase *); MAIN(syncTest) { @@ -136,18 +139,22 @@ MAIN(syncTest) char myname[] = "sync"; db_field_log *pfl[10]; int i; + dbEventCtx evtctx; testPlan(0); - db_init_events(); + evtctx = db_init_events(); - if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + if (dbReadDatabase(&pdbbase, "syncTest.dbd", + "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR + "../O.Common", NULL)) testAbort("Database description not loaded"); (*pvar_func_syncInitialize)(); /* manually initialize plugin */ - xRecord_registerRecordDeviceDriver(pdbbase); + syncTest_registerRecordDeviceDriver(pdbbase); - if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + if (dbReadDatabase(&pdbbase, "xRecord.db", + "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Test database not loaded"); testOk(!!(plug = dbFindFilter(myname, strlen(myname))), "plugin %s registered correctly", myname); @@ -194,6 +201,8 @@ MAIN(syncTest) for (i = 0; i < 10; i++) db_delete_field_log(pfl[i]); + dbChannelDelete(pch); + /* mode UNLESS */ testHead("Mode UNLESS (m='unless', s='red')"); @@ -225,6 +234,8 @@ MAIN(syncTest) for (i = 0; i < 10; i++) db_delete_field_log(pfl[i]); + dbChannelDelete(pch); + /* mode BEFORE */ testHead("Mode BEFORE (m='before', s='red')"); @@ -254,8 +265,9 @@ MAIN(syncTest) mustDrop(pch, pfl[8], "state=FALSE, log8"); mustDrop(pch, pfl[9], "state=FALSE, log9"); - for (i = 0; i < 10; i++) - db_delete_field_log(pfl[i]); + db_delete_field_log(pfl[2]); + + dbChannelDelete(pch); /* mode FIRST */ @@ -285,8 +297,10 @@ MAIN(syncTest) mustDrop(pch, pfl[7], "state=FALSE, log7"); mustDrop(pch, pfl[8], "state=FALSE, log8"); - for (i = 0; i < 10; i++) - db_delete_field_log(pfl[i]); + db_delete_field_log(pfl[3]); + db_delete_field_log(pfl[9]); + + dbChannelDelete(pch); /* mode LAST */ @@ -317,8 +331,9 @@ MAIN(syncTest) mustDrop(pch, pfl[8], "state=FALSE, log8"); mustDrop(pch, pfl[9], "state=FALSE, log9"); - for (i = 0; i < 10; i++) - db_delete_field_log(pfl[i]); + db_delete_field_log(pfl[5]); + + dbChannelDelete(pch); /* mode AFTER */ @@ -348,20 +363,17 @@ MAIN(syncTest) mustDrop(pch, pfl[7], "state=FALSE, log7"); mustDrop(pch, pfl[8], "state=FALSE, log8"); - for (i = 0; i < 10; i++) - db_delete_field_log(pfl[i]); + db_delete_field_log(pfl[6]); + db_delete_field_log(pfl[9]); dbChannelDelete(pch); dbFreeBase(pdbbase); + registryFree(); + pdbbase=0; + + db_close_events(evtctx); + + dbmfFreeChunks(); return testDone(); } - -#define GEN_SIZE_OFFSET -#include "xRecord.h" - -#include -#include - -static rset xRSET; -epicsExportAddress(rset,xRSET); diff --git a/src/std/filters/test/tsTest.c b/src/std/filters/test/tsTest.c index 9c54063ce..9c1441f8d 100644 --- a/src/std/filters/test/tsTest.c +++ b/src/std/filters/test/tsTest.c @@ -16,8 +16,11 @@ #include "dbAccessDefs.h" #include "chfPlugin.h" #include "epicsUnitTest.h" +#include "registry.h" +#include "dbmf.h" #include "epicsTime.h" #include "testMain.h" +#include "osiFileName.h" #define PATTERN 0x55 @@ -36,7 +39,7 @@ static int fl_equal_ex_ts(const db_field_log *pfl1, const db_field_log *pfl2) { return fl_equal(&fl1, pfl2); } -void xRecord_registerRecordDeviceDriver(struct dbBase *); +void tsTest_registerRecordDeviceDriver(struct dbBase *); MAIN(tsTest) { @@ -50,18 +53,22 @@ MAIN(tsTest) db_field_log fl1; db_field_log *pfl2; epicsTimeStamp stamp, now; + dbEventCtx evtctx; testPlan(12); - db_init_events(); + evtctx = db_init_events(); - if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + if (dbReadDatabase(&pdbbase, "tsTest.dbd", + "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR + "../O.Common", NULL)) testAbort("Database description not loaded"); (*pvar_func_tsInitialize)(); /* manually initialize plugin */ - xRecord_registerRecordDeviceDriver(pdbbase); + tsTest_registerRecordDeviceDriver(pdbbase); - if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + if (dbReadDatabase(&pdbbase, "xRecord.db", + "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Test database not loaded"); testOk(!!(plug = dbFindFilter(ts, strlen(ts))), "plugin ts registered correctly"); @@ -92,6 +99,8 @@ MAIN(tsTest) testOk(!!(pfl2 = db_create_read_log(pch)), "create field log from channel"); stamp = pfl2->time; + db_delete_field_log(pfl2); + pfl2 = dbChannelRunPreChain(pch, &fl1); epicsTimeGetCurrent(&now); testOk(epicsTimeDiffInSeconds(&pfl2->time, &stamp) > 0. && @@ -99,15 +108,12 @@ MAIN(tsTest) dbChannelDelete(pch); dbFreeBase(pdbbase); + registryFree(); + pdbbase=0; + + db_close_events(evtctx); + + dbmfFreeChunks(); return testDone(); } - -#define GEN_SIZE_OFFSET -#include "xRecord.h" - -#include -#include - -static rset xRSET; -epicsExportAddress(rset,xRSET); diff --git a/src/std/filters/test/xRecord.c b/src/std/filters/test/xRecord.c new file mode 100644 index 000000000..568fbb838 --- /dev/null +++ b/src/std/filters/test/xRecord.c @@ -0,0 +1,25 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Andrew Johnson + * Ralph Lange + */ + +#include "dbAccessDefs.h" +#include + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/std/filters/test/dbChannelTest.db b/src/std/filters/test/xRecord.db similarity index 100% rename from src/std/filters/test/dbChannelTest.db rename to src/std/filters/test/xRecord.db