11 Commits

Author SHA1 Message Date
Michael Davidsaver b1d28cca39 Update version numbers for release 2020-07-26 13:41:51 -07:00
Michael Davidsaver 50be5b6025 release notes 2020-07-11 14:08:26 -07:00
Michael Davidsaver 8e7a1ad295 checkDISP 2020-07-05 23:06:30 -07:00
Michael Davidsaver e894ae753f create second dbChannel for DBE_PROPERTY for groups 2020-07-05 23:06:30 -07:00
Michael Davidsaver 4559f483bf create second dbChannel for DBE_PROPERTY
Create second channel, and second set of filter states,
for subscription to DBE_PROPERTY.  Accommodate filters
which drop events.
2020-07-02 22:45:58 -07:00
Michael Davidsaver e843db5d27 softMain compat 2020-06-01 19:54:08 -07:00
Michael Davidsaver 8363c870bd adapt softMain as softIocPVA 2020-06-01 19:54:08 -07:00
Michael Davidsaver 9499137bb4 update softMain.cpp from Base circa 7.0.4 2020-06-01 19:54:08 -07:00
Michael Davidsaver c4798dc2bc shareLib.h cleanup 2020-06-01 18:22:08 -07:00
Michael Davidsaver 11eeed9d0c notes 2020-06-01 18:22:08 -07:00
Andrew Johnson 94d1eedc75 Set next development version 2020-05-28 16:26:01 -05:00
19 changed files with 281 additions and 233 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = pva2pva
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.2.3
PROJECT_NUMBER = 1.2.4
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
-2
View File
@@ -16,8 +16,6 @@
#include "weakmap.h"
#include "weakset.h"
#include <shareLib.h>
struct TestPV;
struct TestPVChannel;
struct TestPVMonitor;
+1 -1
View File
@@ -1,7 +1,7 @@
# Module (source) version
EPICS_QSRV_MAJOR_VERSION = 1
EPICS_QSRV_MINOR_VERSION = 2
EPICS_QSRV_MAINTENANCE_VERSION = 3
EPICS_QSRV_MAINTENANCE_VERSION = 4
# ABI version
EPICS_QSRV_ABI_MAJOR_VERSION = 1
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = pva2pva
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.2.3
PROJECT_NUMBER = 1.2.4
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+9
View File
@@ -2,6 +2,15 @@
@page release_notes Release Notes
Release 1.2.4 (July 2020)
=========================
- Bug Fixes
- Fix stalled monitor when server side filter drops initial DBE_PROPERTY update.
- Respect DISP
- Changes
- Refreshed softIocPVA to match options in Base.
Release 1.2.3 (May 2020)
========================
-2
View File
@@ -14,8 +14,6 @@
#include "weakmap.h"
#include "weakset.h"
#include <shareLib.h>
struct ChannelCache;
struct ChannelCacheEntry;
struct MonitorUser;
-2
View File
@@ -3,8 +3,6 @@
#include <pv/pvAccess.h>
#include <shareLib.h>
#include "chancache.h"
struct GWChannel : public epics::pvAccess::Channel
-2
View File
@@ -5,8 +5,6 @@
#include <pv/pvAccess.h>
#include <shareLib.h>
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
-2
View File
@@ -6,8 +6,6 @@
#include "chancache.h"
#include "channel.h"
#include <shareLib.h>
struct GWServerChannelProvider :
public epics::pvAccess::ChannelProvider,
public epics::pvAccess::ChannelFind,
+3 -1
View File
@@ -60,7 +60,9 @@ qsrv_LIBS += $(EPICS_BASE_IOC_LIBS)
FINAL_LOCATION ?= $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION))
USR_CPPFLAGS += -DFINAL_LOCATION="\"$(FINAL_LOCATION)\""
# since we copy softMain.cpp from Base, use the same
# EPICS_BASE macro for our location
USR_CPPFLAGS += -DEPICS_BASE="\"$(FINAL_LOCATION)\""
PROD_IOC += softIocPVA
+8 -1
View File
@@ -419,6 +419,12 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::const_shared_poin
members_map[mem.pvfldname] = J;
PDBGroupPV::Info& info = members[J];
DBCH chan2;
if(chan.chan && (ellCount(&chan.chan->pre_chain)>0 || ellCount(&chan.chan->post_chain)>0)) {
DBCH temp(mem.pvname);
info.chan2.swap(chan2);
}
info.allowProc = mem.putorder != std::numeric_limits<int>::min();
info.builder = PTRMOVE(mem.builder);
assert(info.builder.get());
@@ -514,7 +520,8 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::const_shared_poin
info.pvif.reset(info.builder->attach(info.chan, pv->complete, info.attachment));
// TODO: don't need evt_PROPERTY for PVIF plain
info.evt_PROPERTY.create(event_context, info.chan, &pdb_group_event, DBE_PROPERTY);
dbChannel *pchan = info.chan2.chan ? info.chan2.chan : info.chan.chan;
info.evt_PROPERTY.create(event_context, pchan, &pdb_group_event, DBE_PROPERTY);
if(!info.triggers.empty()) {
info.evt_VALUE.create(event_context, info.chan, &pdb_group_event, DBE_VALUE|DBE_ALARM);
+1 -1
View File
@@ -8,7 +8,7 @@
#include "weakmap.h"
#include <shareLib.h>
#include <pv/qsrv.h>
struct PDBProvider;
+2 -2
View File
@@ -17,8 +17,6 @@
#include "pvif.h"
#include "pdb.h"
#include <shareLib.h>
struct QSRV_API GroupConfig
{
struct QSRV_API Field {
@@ -89,6 +87,8 @@ struct QSRV_API PDBGroupPV : public PDBPV
struct Info {
DBCH chan;
// used for DBE_PROPERTY subscription when chan has filters
DBCH chan2;
std::tr1::shared_ptr<PVIFBuilder> builder;
FieldName attachment;
typedef std::vector<size_t> triggers_t;
+7 -2
View File
@@ -103,6 +103,10 @@ PDBSinglePV::PDBSinglePV(DBCH& chan,
,hadevent_VALUE(false)
,hadevent_PROPERTY(false)
{
if(ellCount(&chan.chan->pre_chain) || ellCount(&chan.chan->post_chain)) {
DBCH temp(dbChannelName(chan.chan));
this->chan2.swap(temp);
}
this->chan.swap(chan);
fielddesc = std::tr1::static_pointer_cast<const pvd::Structure>(builder->dtype(this->chan));
@@ -120,8 +124,9 @@ PDBSinglePV::~PDBSinglePV()
void PDBSinglePV::activate()
{
dbChannel *pchan = this->chan2.chan ? this->chan2.chan : this->chan.chan;
evt_VALUE.create(provider->event_context, this->chan, &pdb_single_event, DBE_VALUE|DBE_ALARM);
evt_PROPERTY.create(provider->event_context, this->chan, &pdb_single_event, DBE_PROPERTY);
evt_PROPERTY.create(provider->event_context, pchan, &pdb_single_event, DBE_PROPERTY);
}
pva::Channel::shared_pointer
@@ -395,7 +400,7 @@ void PDBSinglePut::put(pvd::PVStructure::shared_pointer const & value,
p2p::auto_ptr<PVIF> putpvif(channel->pv->builder->attach(channel->pv->chan, value, FieldName()));
try{
DBScanLocker L(chan);
putpvif->get(*changed, doProc);
ret = putpvif->get(*changed, doProc);
}catch(std::runtime_error& e){
ret = pvd::Status::error(e.what());
+2 -2
View File
@@ -16,8 +16,6 @@
#include "pvif.h"
#include "pdb.h"
#include <shareLib.h>
struct PDBSingleMonitor;
struct QSRV_API PDBSinglePV : public PDBPV
@@ -32,6 +30,8 @@ struct QSRV_API PDBSinglePV : public PDBPV
* is locked.
*/
DBCH chan;
// used for DBE_PROPERTY subscription when chan has filters
DBCH chan2;
PDBProvider::shared_pointer provider;
// only for use in pdb_single_event()
+16 -2
View File
@@ -641,6 +641,14 @@ void findFormat(pvTimeAlarm& pvmeta, pdbRecordIterator& info, const epics::pvDat
}
}
pvd::Status checkDISP(dbChannel *chan)
{
dbCommon *prec = dbChannelRecord(chan);
pvd::Status ret;
if(prec->disp && dbChannelField(chan)!=&prec->disp)
ret = pvd::Status::error("Put Disabled");
return ret;
}
template<typename PVX, typename META>
struct PVIFScalarNumeric : public PVIF
@@ -698,7 +706,10 @@ struct PVIFScalarNumeric : public PVIF
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
{
pvd::Status ret;
pvd::Status ret = checkDISP(chan);
if(!ret)
return ret;
bool newval = mask.logical_and(pvmeta.maskVALUEPut);
if(newval) {
if(permit)
@@ -906,7 +917,10 @@ struct PVIFPlain : public PVIF
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
{
pvd::Status ret;
pvd::Status ret = checkDISP(chan);
if(!ret)
return ret;
bool newval = mask.get(fieldOffset);
if(newval) {
if(permit)
+13
View File
@@ -378,6 +378,18 @@ private:
PVIF& operator=(const PVIF&);
};
/** Factory for PVIF instances.
*
* Caller first passes a mapping type (eg. "scalar") to PVIFBuilder::create()
* to obtain a PVIFBuilder which may then by used to create PVIF instances
* for specific dbChannel.
*
* Caller than uses PVIFBuilder::dtype() to obtain (sub)Field descriptions.
* eg. more than one of these may be composed into an overall Structure description.
*
* Caller than creates a PVStructure and uses PVIFBuilder::attach() to
* build mappings for each dbChannel in the composed locations.
*/
struct QSRV_API PVIFBuilder {
virtual ~PVIFBuilder() {}
@@ -394,6 +406,7 @@ struct QSRV_API PVIFBuilder {
// must be the root structure
virtual PVIF* attach(dbChannel *channel, const epics::pvData::PVStructurePtr& root, const FieldName& fld) =0;
// entry point for Builder
static PVIFBuilder* create(const std::string& name);
protected:
PVIFBuilder() {}
+217 -207
View File
@@ -7,242 +7,252 @@
* found in the file LICENSE that is included with this distribution.
\*************************************************************************/
/* Copyed from EPICS Base 3.16 branch */
/* Author: Andrew Johnson Date: 2003-04-08 */
/* Author: Andrew Johnson Date: 2003-04-08 */
/* Usage:
* softIoc [-D softIoc.dbd] [-h] [-S] [-s] [-a ascf]
* [-m macro=value,macro2=value2] [-d file.db]
* [-x prefix] [st.cmd]
*
* If used the -D option must come first, and specify the
* path to the softIoc.dbd file. The compile-time install
* location is saved in the binary as a default.
*
* Usage information will be printed if -h is given, then
* the program will exit normally.
*
* The -S option prevents an interactive shell being started
* after all arguments have been processed.
*
* Previous versions accepted a -s option to cause a shell
* to be started; this option is still accepted but ignored
* since a command shell is now started by default.
*
* Access Security can be enabled with the -a option giving
* the name of the configuration file; if any macros were
* set with -m before the -a option was given, they will be
* used as access security substitution macros.
*
* Any number of -m and -d arguments can be interspersed;
* the macros are applied to the following .db files. Each
* later -m option causes earlier macros to be discarded.
*
* The -x option loads the softIocExit.db with the macro
* IOC set to the string provided. This database contains
* a subroutine record named $(IOC):exit which has its field
* SNAM set to "exit". When this record is processed, the
* subroutine that runs will call epicsExit() with the value
* of the field A determining whether the exit status is
* EXIT_SUCCESS if (A == 0.0) or EXIT_FAILURE (A != 0.0).
*
* A st.cmd file is optional. If any databases were loaded
* the st.cmd file will be run *after* iocInit. To perform
* iocsh commands before iocInit, all database loading must
* be performed by the script itself, or by the user from
* the interactive IOC shell.
*/
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <list>
#include <stdexcept>
#include <epicsVersion.h>
#include <epicsGetopt.h>
#include "registryFunction.h"
#include "epicsThread.h"
#include "epicsExit.h"
#include "epicsStdio.h"
#include "epicsString.h"
#include "dbStaticLib.h"
#include "subRecord.h"
#include "dbAccess.h"
#include "asDbLib.h"
#include "iocInit.h"
#include "iocsh.h"
#include "osiFileName.h"
extern "C" int softIocPVA_registerRecordDeviceDriver(struct dbBase *pdbbase);
#ifdef __rtems__
#define DBD_FILE "dbd/softIocPVA.dbd"
#define EXIT_FILE "db/softIocExit.db"
#else
#define DBD_FILE FINAL_LOCATION "/dbd/softIocPVA.dbd"
#define EXIT_FILE FINAL_LOCATION "/db/softIocExit.db"
#ifndef EPICS_BASE
// so IDEs knows EPICS_BASE is a string constant
# define EPICS_BASE "/"
# error -DEPICS_BASE required
#endif
#ifdef VERSION_INT
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
#define USE_EXIT_LATER
#if EPICS_VERSION_INT>=VERSION_INT(7,0,2,0)
# define USE_EXECDIR
#endif
#endif
const char *arg0;
const char *base_dbd = DBD_FILE;
const char *exit_db = EXIT_FILE;
#define DBD_BASE "dbd" OSI_PATH_SEPARATOR "softIocPVA.dbd"
#define EXIT_BASE "db" OSI_PATH_SEPARATOR "softIocExit.db"
#define DBD_FILE_REL ".." OSI_PATH_SEPARATOR ".." OSI_PATH_SEPARATOR DBD_BASE
#define EXIT_FILE_REL ".." OSI_PATH_SEPARATOR ".." OSI_PATH_SEPARATOR EXIT_BASE
#define DBD_FILE EPICS_BASE OSI_PATH_SEPARATOR DBD_BASE
#define EXIT_FILE EPICS_BASE OSI_PATH_SEPARATOR EXIT_BASE
namespace {
static void exitSubroutine(subRecord *precord) {
#ifdef USE_EXIT_LATER
epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
#else
epicsExit((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
#endif
}
static void usage(int status) {
printf("Usage: %s [-D softIoc.dbd] [-h] [-S] [-a ascf]\n", arg0);
puts("\t[-m macro=value,macro2=value2] [-d file.db]");
puts("\t[-x prefix] [st.cmd]");
puts("Compiled-in path to softIocPVA.dbd is:");
printf("\t%s\n", base_dbd);
epicsExit(status);
void usage(const char *arg0, const std::string& base_dbd) {
std::cout<<"Usage: "<<arg0<<
" [-D softIocPVA.dbd] [-h] [-S] [-s] [-a ascf]\n"
"[-m macro=value,macro2=value2] [-d file.db]\n"
"[-x prefix] [st.cmd]\n"
"\n"
" -D <dbd> If used, must come first. Specify the path to the softIocPVA.dbdfile."
" The compile-time install location is saved in the binary as a default.\n"
"\n"
" -h Print this mesage and exit.\n"
"\n"
" -S Prevents an interactive shell being started.\n"
"\n"
" -s Previously caused a shell to be started. Now accepted and ignored.\n"
"\n"
" -a <acf> Access Security configuration file. Macro substitution is\n"
" performed.\n"
"\n"
" -m <MAC>=<value>,... Set/replace macro definitions used by subsequent -d and\n"
" -a.\n"
"\n"
" -d <db> Load records from file (dbLoadRecords). Macro substitution is\n"
" performed.\n"
"\n"
" -x <prefix> Load softIocExit.db. Provides a record \"<prefix>:exit\".\n"
" Put 0 to exit with success, or non-zero to exit with an error.\n"
"\n"
"Any number of -m and -d arguments can be interspersed; the macros are applied\n"
"to the following .db files. Each later -m option causes earlier macros to be\n"
"discarded.\n"
"\n"
"A st.cmd file is optional. If any databases were loaded the st.cmd file will\n"
"be run *after* iocInit. To perform iocsh commands before iocInit, all database\n"
"loading must be performed by the script itself, or by the user from the\n"
"interactive IOC shell.\n"
"\n"
"Compiled-in path to softIocPVA.dbd is:\n"
"\t"<<base_dbd.c_str()<<"\n";
}
void errIf(int ret, const std::string& msg)
{
if(ret)
throw std::runtime_error(msg);
}
bool lazy_dbd_loaded;
void lazy_dbd(const std::string& dbd_file) {
if(lazy_dbd_loaded) return;
lazy_dbd_loaded = true;
errIf(dbLoadDatabase(dbd_file.c_str(), NULL, NULL),
std::string("Failed to load DBD file: ")+dbd_file);
std::cout<<"dbLoadDatabase(\""<<dbd_file<<"\")\n";
softIocPVA_registerRecordDeviceDriver(pdbbase);
std::cout<<"softIocPVA_registerRecordDeviceDriver(pdbbase)\n";
registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine);
}
} // namespace
int main(int argc, char *argv[])
{
char *dbd_file = const_cast<char*>(base_dbd);
char *macros = NULL;
char xmacro[PVNAME_STRINGSZ + 4];
int startIocsh = 1; /* default = start shell */
int loadedDb = 0;
arg0 = strrchr(*argv, '/');
if (!arg0) {
arg0 = *argv;
} else {
++arg0; /* skip the '/' */
}
--argc, ++argv;
/* Do this here in case the dbd file not available */
if (argc>0 && **argv=='-' && (*argv)[1]=='h') {
usage(EXIT_SUCCESS);
}
if (argc>1 && **argv=='-' && (*argv)[1]=='D') {
dbd_file = *++argv;
argc -= 2;
++argv;
}
if (dbLoadDatabase(dbd_file, NULL, NULL)) {
epicsExit(EXIT_FAILURE);
}
softIocPVA_registerRecordDeviceDriver(pdbbase);
registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine);
try {
std::string dbd_file(DBD_FILE),
exit_file(EXIT_FILE),
macros, // scratch space for macros (may be given more than once)
xmacro;
bool interactive = true;
bool loadedDb = false;
while (argc>1 && **argv == '-') {
switch ((*argv)[1]) {
case 'a':
if (macros) asSetSubstitutions(macros);
asSetFilename(*++argv);
--argc;
break;
case 'd':
if (dbLoadRecords(*++argv, macros)) {
epicsExit(EXIT_FAILURE);
}
loadedDb = 1;
--argc;
break;
case 'h':
usage(EXIT_SUCCESS);
case 'm':
macros = *++argv;
--argc;
break;
case 'S':
startIocsh = 0;
break;
case 's':
break;
case 'x':
epicsSnprintf(xmacro, sizeof xmacro, "IOC=%s", *++argv);
if (dbLoadRecords(exit_db, xmacro)) {
epicsExit(EXIT_FAILURE);
}
loadedDb = 1;
--argc;
break;
default:
printf("%s: option '%s' not recognized\n", arg0, *argv);
usage(EXIT_FAILURE);
}
--argc;
++argv;
#ifdef USE_EXECDIR
// attempt to compute relative paths
{
std::string prefix;
char *cprefix = epicsGetExecDir();
if(cprefix) {
try {
prefix = cprefix;
free(cprefix);
} catch(...) {
free(cprefix);
throw;
}
}
dbd_file = prefix + DBD_FILE_REL;
exit_file = prefix + EXIT_FILE_REL;
}
#endif
int opt;
while ((opt = getopt(argc, argv, "ha:D:d:m:Ssx:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
usage(argv[0], dbd_file);
epicsExit(0);
return 0;
default:
usage(argv[0], dbd_file);
std::cerr<<"Unknown argument: -"<<char(opt)<<"\n";
epicsExit(2);
return 2;
case 'a':
lazy_dbd(dbd_file);
if (!macros.empty()) {
if(asSetSubstitutions(macros.c_str()))
throw std::bad_alloc();
std::cout<<"asSetSubstitutions(\""<<macros<<"\")\n";
}
if(asSetFilename(optarg))
throw std::bad_alloc();
std::cout<<"asSetFilename(\""<<optarg<<"\")\n";
break;
case 'D':
if(lazy_dbd_loaded) {
throw std::runtime_error("-D specified too late. softIocPVA.dbd already loaded.\n");
}
dbd_file = optarg;
break;
case 'd':
lazy_dbd(dbd_file);
errIf(dbLoadRecords(optarg, macros.c_str()),
std::string("Failed to load: ")+optarg);
std::cout<<"dbLoadRecords(\""<<optarg<<"\"";
if(!macros.empty())
std::cout<<", \""<<macros<<"\"";
std::cout<<")\n";
loadedDb = true;
break;
case 'm':
macros = optarg;
break;
case 'S':
interactive = false;
break;
case 's':
break; // historical
case 'x':
lazy_dbd(dbd_file);
xmacro = "IOC=";
xmacro += optarg;
errIf(dbLoadRecords(exit_file.c_str(), xmacro.c_str()),
std::string("Failed to load: ")+exit_file);
loadedDb = true;
break;
}
}
lazy_dbd(dbd_file);
if(optind<argc) {
// run script
// ignore any extra positional args (historical)
std::cout<<"# Begin "<<argv[optind]<<"\n";
errIf(iocsh(argv[optind]),
std::string("Error in ")+argv[optind]);
std::cout<<"# End "<<argv[optind]<<"\n";
epicsThreadSleep(0.2);
loadedDb = true; /* Give it the benefit of the doubt... */
}
if (loadedDb) {
std::cout<<"iocInit()\n";
iocInit();
epicsThreadSleep(0.2);
}
if(interactive) {
std::cout.flush();
std::cerr.flush();
if(iocsh(NULL)) {
epicsExit(1);
return 1;
}
} else {
if (loadedDb) {
epicsThreadExitMain();
} else {
usage(argv[0], dbd_file);
std::cerr<<"Nothing to do!\n";
epicsExit(1);
return 1;
}
}
epicsExit(0);
return 0;
}catch(std::exception& e){
std::cerr<<"Error: "<<e.what()<<"\n";
epicsExit(2);
return 2;
}
if (argc>0 && **argv=='-') {
switch((*argv)[1]) {
case 'a':
case 'd':
case 'm':
case 'x':
printf("%s: missing argument to option '%s'\n", arg0, *argv);
usage(EXIT_FAILURE);
case 'h':
usage(EXIT_SUCCESS);
case 'S':
startIocsh = 0;
break;
case 's':
break;
default:
printf("%s: option '%s' not recognized\n", arg0, *argv);
usage(EXIT_FAILURE);
}
--argc;
++argv;
}
if (loadedDb) {
iocInit();
epicsThreadSleep(0.2);
}
/* run user's startup script */
if (argc>0) {
if (iocsh(*argv)) epicsExit(EXIT_FAILURE);
epicsThreadSleep(0.2);
loadedDb = 1; /* Give it the benefit of the doubt... */
}
/* start an interactive shell if it was requested */
if (startIocsh) {
iocsh(NULL);
} else {
if (loadedDb) {
epicsThreadExitMain();
} else {
printf("%s: Nothing to do!\n", arg0);
usage(EXIT_FAILURE);
}
}
epicsExit(EXIT_SUCCESS);
/*Note that the following statement will never be executed*/
return 0;
}
-2
View File
@@ -13,8 +13,6 @@
#include <pv/sharedPtr.h>
#include <shareLib.h>
struct WorkQueue : private epicsThreadRunable
{
typedef std::tr1::weak_ptr<epicsThreadRunable> value_type;