Merged changes from 3.14 branch up to 2011-11-02

This commit is contained in:
Andrew Johnson
2011-11-02 14:59:18 -05:00
14 changed files with 587 additions and 215 deletions

View File

@@ -1,10 +1,9 @@
#*************************************************************************
# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
# Copyright (c) 2011 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.
# EPICS BASE Versions 3.13.7
# and higher are distributed subject to a Software License Agreement found
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
#RULES.Db
@@ -23,8 +22,8 @@ vpath %.acs $(USR_VPATH) $(GENERIC_SRC_DIRS) $(COMMON_DIR)
##################################################### dbdflags
# dbExpand
INSTALL_DBDFLAGS += -I $(INSTALL_LOCATION)/dbd
INSTALL_DBFLAGS += -I $(INSTALL_LOCATION)/db
INSTALL_DBDFLAGS += -I $(INSTALL_DBD)
INSTALL_DBFLAGS += -I $(INSTALL_DB)
DBDFLAGS = $(USR_DBDFLAGS) -I . -I .. $(INSTALL_DBDFLAGS) $(RELEASE_DBDFLAGS)
DBFLAGS = $($*_DBFLAGS) $(USR_DBFLAGS) -I. -I.. $(INSTALL_DBFLAGS) $(RELEASE_DBFLAGS)

View File

@@ -85,7 +85,7 @@ PROD_LIBS = ca Com
PROD_SYS_LIBS_WIN32 = ws2_32 advapi32 user32
PROD_HOST += caRepeater catime acctst caConnTest casw caEventRate
OBJS_IOC_vxWorks += catime acctst caConnTest casw caEventRate
OBJS_IOC += catime acctst caConnTest casw caEventRate acctstRegister
caRepeater_SRCS = caRepeater.cpp
catime_SRCS = catimeMain.c catime.c
acctst_SRCS = acctstMain.c acctst.c

View File

@@ -10,6 +10,10 @@
/*
* CA regression test
* Authors:
* Jeff Hill
* Murali Shankar - initial versions of verifyMultithreadSubscr
*
*/
/*
@@ -26,6 +30,8 @@
*/
#define epicsAssertAuthor "Jeff Hill johill@lanl.gov"
#include "epicsAssert.h"
#include "epicsMutex.h"
#include "epicsEvent.h"
#include "epicsTime.h"
#include "dbDefs.h"
#include "envDefs.h"
@@ -2719,6 +2725,186 @@ void fdRegCB ( void * parg, int fd, int opened )
}
}
typedef struct {
char m_chanName[100u];
struct ca_client_context * m_pCtx;
chid m_chan;
epicsMutexId m_mutex;
epicsEventId m_testCompleteEvent;
epicsEventId m_threadExitEvent;
size_t m_nUpdatesReceived;
size_t m_nUpdatesRequired;
int m_testInitiated;
int m_testComplete;
unsigned m_interestLevel;
} MultiThreadSubscrTest;
static void testMultithreadSubscrSubscrCallback
( struct event_handler_args eha )
{
const epicsEventId firstUpdateEvent = ( epicsEventId ) eha.usr;
epicsEventSignal ( firstUpdateEvent );
}
static void testMultithreadSubscrCreateSubscr ( void * pParm )
{
static unsigned nElem = 0;
int testComplete = FALSE;
evid id;
epicsEventId firstUpdateEvent;
epicsEventWaitStatus eventWaitStatus;
MultiThreadSubscrTest * const pMultiThreadSubscrTest =
( MultiThreadSubscrTest * ) pParm;
/* this is required for the ca_flush below to work correctly */
int status = ca_attach_context ( pMultiThreadSubscrTest->m_pCtx );
verify ( status == ECA_NORMAL );
firstUpdateEvent = epicsEventMustCreate ( epicsEventEmpty );
verify ( firstUpdateEvent );
status = ca_create_subscription (
DBR_TIME_LONG,
nElem,
pMultiThreadSubscrTest->m_chan,
DBE_VALUE,
testMultithreadSubscrSubscrCallback,
firstUpdateEvent,
& id );
verify ( status == ECA_NORMAL );
status = ca_flush_io ();
verify ( status == ECA_NORMAL );
/* wait for first update */
eventWaitStatus = epicsEventWaitWithTimeout (
firstUpdateEvent, 60.0 * 10 );
verify ( eventWaitStatus == epicsEventWaitOK );
epicsEventDestroy ( firstUpdateEvent );
status = ca_clear_subscription ( id );
verify ( status == ECA_NORMAL );
epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex );
pMultiThreadSubscrTest->m_nUpdatesReceived++;
testComplete = ( pMultiThreadSubscrTest->m_nUpdatesReceived ==
pMultiThreadSubscrTest->m_nUpdatesRequired );
pMultiThreadSubscrTest->m_testComplete = testComplete;
epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex );
if ( testComplete ) {
epicsEventSignal ( pMultiThreadSubscrTest->m_testCompleteEvent );
}
}
void testMultithreadSubscrConnHandler ( struct connection_handler_args args )
{
MultiThreadSubscrTest * const pMultiThreadSubscrTest =
( MultiThreadSubscrTest * ) ca_puser ( args.chid );
epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex );
if ( !pMultiThreadSubscrTest->m_testInitiated &&
args.op == CA_OP_CONN_UP ) {
int i;
pMultiThreadSubscrTest->m_testInitiated = TRUE;
for ( i = 0; i < pMultiThreadSubscrTest->m_nUpdatesRequired; i++ ) {
char threadname[64];
epicsThreadId threadId;
sprintf(threadname, "testSubscr%06u", i);
threadId = epicsThreadCreate ( threadname,
epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackSmall),
testMultithreadSubscrCreateSubscr,
pMultiThreadSubscrTest );
verify ( threadId );
}
}
epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex );
}
void testMultithreadSubscr ( void * pParm )
{
MultiThreadSubscrTest * const pMultiThreadSubscrTest =
( MultiThreadSubscrTest * ) pParm;
int status;
unsigned i;
status = ca_context_create ( ca_enable_preemptive_callback );
verify ( status == ECA_NORMAL );
pMultiThreadSubscrTest->m_pCtx = ca_current_context ();
verify ( pMultiThreadSubscrTest->m_pCtx );
status = ca_create_channel (
pMultiThreadSubscrTest->m_chanName,
testMultithreadSubscrConnHandler,
pMultiThreadSubscrTest,
CA_PRIORITY_MIN,
& pMultiThreadSubscrTest->m_chan );
verify ( status == ECA_NORMAL );
showProgressBegin ( "verifyMultithreadSubscr",
pMultiThreadSubscrTest->m_interestLevel );
i = 0;
while ( TRUE ) {
int success = FALSE;
epicsEventWaitStatus eventWaitStatus;
epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex );
success = pMultiThreadSubscrTest->m_testComplete;
epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex );
if ( success ) {
break;
}
eventWaitStatus = epicsEventWaitWithTimeout (
pMultiThreadSubscrTest->m_testCompleteEvent, 0.1 );
verify ( eventWaitStatus == epicsEventWaitOK ||
eventWaitStatus == epicsEventWaitTimeout );
if ( i++ % 100 == 0u )
showProgress ( pMultiThreadSubscrTest->m_interestLevel );
verify ( i < 1000 );
}
showProgressEnd ( pMultiThreadSubscrTest->m_interestLevel );
status = ca_clear_channel ( pMultiThreadSubscrTest->m_chan );
verify ( status == ECA_NORMAL );
ca_context_destroy ();
epicsEventSignal ( pMultiThreadSubscrTest->m_threadExitEvent );
}
/*
* test installation of subscriptions similar to usage paterns
* employed by modern versions of the sequencer
*/
void verifyMultithreadSubscr ( const char * pName, unsigned interestLevel )
{
static unsigned nSubscr = 3000;
epicsThreadId threadId;
MultiThreadSubscrTest * const pMultiThreadSubscrTest =
(MultiThreadSubscrTest*) calloc ( 1,
sizeof ( MultiThreadSubscrTest ) );
verify ( pMultiThreadSubscrTest);
pMultiThreadSubscrTest->m_mutex = epicsMutexMustCreate ();
verify ( pMultiThreadSubscrTest->m_mutex );
pMultiThreadSubscrTest->m_testCompleteEvent =
epicsEventMustCreate ( epicsEventEmpty );
verify ( pMultiThreadSubscrTest->m_testCompleteEvent );
pMultiThreadSubscrTest->m_threadExitEvent =
epicsEventMustCreate ( epicsEventEmpty );
verify ( pMultiThreadSubscrTest->m_threadExitEvent );
strncpy ( pMultiThreadSubscrTest->m_chanName, pName,
sizeof ( pMultiThreadSubscrTest->m_chanName ) );
pMultiThreadSubscrTest->m_chanName
[ sizeof ( pMultiThreadSubscrTest->m_chanName ) - 1u ] = '\0';
pMultiThreadSubscrTest->m_nUpdatesRequired = nSubscr;
pMultiThreadSubscrTest->m_interestLevel = interestLevel;
threadId = epicsThreadCreate (
"testMultithreadSubscr",
epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackSmall),
testMultithreadSubscr, pMultiThreadSubscrTest );
verify ( threadId );
{
epicsEventWaitStatus eventWaitStatus;
eventWaitStatus = epicsEventWaitWithTimeout (
pMultiThreadSubscrTest->m_threadExitEvent, 1000.0 );
verify ( eventWaitStatus == epicsEventWaitOK );
}
epicsEventDestroy ( pMultiThreadSubscrTest->m_testCompleteEvent );
epicsEventDestroy ( pMultiThreadSubscrTest->m_threadExitEvent );
epicsMutexDestroy ( pMultiThreadSubscrTest->m_mutex );
free ( pMultiThreadSubscrTest );
}
void fdManagerVerify ( const char * pName, unsigned interestLevel )
{
int status;
@@ -2965,17 +3151,22 @@ void verifyContextRundownFlush ( const char * pName, unsigned interestLevel )
SEVCHK ( status, "context create failed" );
status = ca_create_channel ( pName, 0, 0, 0, & chan );
SEVCHK ( status, NULL );
status = ca_pend_io( timeoutToPendIO );
SEVCHK ( status, "channel connect failed" );
status = ca_put ( DBR_DOUBLE, chan, & stim );
SEVCHK ( status, "channel put failed" );
status = ca_clear_channel ( chan );
SEVCHK ( status, NULL );
/*
* currently in-memory channels cant be used with this test
* !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!!
*/
if ( status != ECA_UNAVAILINSERV ) {
SEVCHK ( status, NULL );
status = ca_pend_io( timeoutToPendIO );
SEVCHK ( status, "channel connect failed" );
status = ca_put ( DBR_DOUBLE, chan, & stim );
SEVCHK ( status, "channel put failed" );
status = ca_clear_channel ( chan );
SEVCHK ( status, NULL );
}
ca_context_destroy ();
}
@@ -2985,24 +3176,28 @@ void verifyContextRundownFlush ( const char * pName, unsigned interestLevel )
dbr_double_t resp;
status = ca_context_create ( ca_disable_preemptive_callback );
SEVCHK ( status, "context create failed" );
status = ca_create_channel ( pName, 0, 0, 0, & chan );
SEVCHK ( status, NULL );
status = ca_pend_io( timeoutToPendIO );
SEVCHK ( status, "channel connect failed" );
status = ca_get ( DBR_DOUBLE, chan, & resp );
SEVCHK ( status, "channel get failed" );
status = ca_pend_io ( timeoutToPendIO );
SEVCHK ( status, "get, pend io failed" );
verify ( stim == resp );
status = ca_clear_channel ( chan );
SEVCHK ( status, NULL );
/*
* currently in-memory channels cant be used with this test
* !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!!
*/
if ( status != ECA_UNAVAILINSERV ) {
status = ca_pend_io( timeoutToPendIO );
SEVCHK ( status, "channel connect failed" );
status = ca_get ( DBR_DOUBLE, chan, & resp );
SEVCHK ( status, "channel get failed" );
status = ca_pend_io ( timeoutToPendIO );
SEVCHK ( status, "get, pend io failed" );
verify ( stim == resp );
status = ca_clear_channel ( chan );
SEVCHK ( status, NULL );
}
ca_context_destroy ();
}
@@ -3028,6 +3223,13 @@ void verifyContextRundownChanStillExist (
for ( i = 0; i < NELEMENTS ( chan ); i++ ) {
status = ca_create_channel ( pName, 0, 0, 0, & chan[i] );
/*
* currently in-memory channels cant be used with this test
* !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!!
*/
if ( status == ECA_UNAVAILINSERV ) {
break;
}
SEVCHK ( status, NULL );
}
@@ -3123,6 +3325,7 @@ int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount,
verifyHighThroughputReadCallback ( chan, interestLevel );
verifyHighThroughputWriteCallback ( chan, interestLevel );
verifyBadString ( chan, interestLevel );
verifyMultithreadSubscr ( pName, interestLevel );
if ( select != ca_enable_preemptive_callback ) {
fdManagerVerify ( pName, interestLevel );
}

View File

@@ -0,0 +1,69 @@
/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* CA client library diagnostics IOC shell registration
* Authors:
* Jeff Hill
*/
#include <iocsh.h>
#include "caDiagnostics.h"
/* Information needed by iocsh */
static const iocshArg acctstArg0 = { "channel name", iocshArgString };
static const iocshArg acctstArg1 = { "interest level", iocshArgInt };
static const iocshArg acctstArg2 = { "channel count", iocshArgInt };
static const iocshArg acctstArg3 = { "repetition count", iocshArgInt };
static const iocshArg acctstArg4 = { "preemptive callback select", iocshArgInt };
static const iocshArg *acctstArgs[] =
{
&acctstArg0,
&acctstArg1,
&acctstArg2,
&acctstArg3,
&acctstArg4
};
static const iocshFuncDef acctstFuncDef = {"acctst", 5, acctstArgs};
/* Wrapper called by iocsh, selects the argument types that print needs */
static void acctstCallFunc(const iocshArgBuf *args) {
if ( args[1].ival < 0 ) {
printf ( "negative interest level not allowed\n" );
return;
}
if ( args[2].ival < 0 ) {
printf ( "negative channel count not allowed\n" );
return;
}
if ( args[3].ival < 0 ) {
printf ( "negative repetition count not allowed\n" );
return;
}
acctst (
args[0].sval, /* channel name */
( unsigned ) args[1].ival, /* interest level */
( unsigned ) args[2].ival, /* channel count */
( unsigned ) args[3].ival, /* repetition count */
( ca_preemptive_callback_select ) args[4].ival ); /* preemptive callback select */
}
struct AutoInit {
AutoInit ();
};
AutoInit :: AutoInit ()
{
iocshRegister ( &acctstFuncDef, acctstCallFunc );
}
AutoInit autoInit;

View File

@@ -185,7 +185,8 @@ epicsShareFunc enum channel_state epicsShareAPI ca_state (chid chan);
epicsShareFunc int epicsShareAPI ca_task_initialize (void);
enum ca_preemptive_callback_select
{ ca_disable_preemptive_callback, ca_enable_preemptive_callback };
epicsShareFunc int epicsShareAPI ca_context_create (enum ca_preemptive_callback_select select);
epicsShareFunc int epicsShareAPI
ca_context_create (enum ca_preemptive_callback_select select);
epicsShareFunc void epicsShareAPI ca_detach_context ();
/************************************************************************/

View File

@@ -126,7 +126,6 @@ static void event_handler (evargs args)
ppv->dbrType = args.type;
ppv->value = calloc(1, dbr_size_n(args.type, args.count));
memcpy(ppv->value, args.dbr, dbr_size_n(args.type, args.count));
ppv->onceConnected = 1;
ppv->nElems = args.count;
nRead++;
}
@@ -159,12 +158,13 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
int n, result;
for (n = 0; n < nPvs; n++) {
unsigned long nElems;
/* Set up pvs structure */
/* -------------------- */
/* Get natural type and array count */
pvs[n].nElems = ca_element_count(pvs[n].chid);
nElems = ca_element_count(pvs[n].chid);
pvs[n].dbfType = ca_field_type(pvs[n].chid);
pvs[n].dbrType = dbrType;
@@ -183,10 +183,6 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
pvs[n].dbrType = DBR_TIME_STRING;
}
}
/* Adjust array count */
if (reqElems > pvs[n].nElems)
reqElems = pvs[n].nElems;
pvs[n].reqElems = reqElems;
/* Issue CA request */
/* ---------------- */
@@ -196,21 +192,24 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
nConn++;
pvs[n].onceConnected = 1;
if (request == callback)
{ /* Event handler will allocate value */
{
/* Event handler will allocate value and set nElems */
pvs[n].reqElems = reqElems > nElems ? nElems : reqElems;
result = ca_array_get_callback(pvs[n].dbrType,
pvs[n].reqElems,
pvs[n].chid,
event_handler,
(void*)&pvs[n]);
} else {
/* Allocate value structure */
/* We allocate value structure and set nElems */
pvs[n].nElems = reqElems && reqElems < nElems ? reqElems : nElems;
pvs[n].value = calloc(1, dbr_size_n(pvs[n].dbrType, pvs[n].nElems));
if(!pvs[n].value) {
fprintf(stderr,"Allocation failed\n");
if (!pvs[n].value) {
fprintf(stderr,"Memory allocation failed\n");
return 1;
}
result = ca_array_get(pvs[n].dbrType,
pvs[n].reqElems,
pvs[n].nElems,
pvs[n].chid,
pvs[n].value);
}
@@ -252,9 +251,6 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
/* -------------- */
for (n = 0; n < nPvs; n++) {
/* Truncate the data printed to what was requested. */
if (pvs[n].reqElems != 0 && pvs[n].nElems > pvs[n].reqElems)
pvs[n].nElems = pvs[n].reqElems;
switch (format) {
case plain: /* Emulate old caget behaviour */
@@ -377,7 +373,7 @@ static void complainIfNotPlainAndSet (OutputT *current, const OutputT requested)
int main (int argc, char *argv[])
{
int n = 0;
int n;
int result; /* CA result */
OutputT format = plain; /* User specified format */
RequestT request = get; /* User specified request type */
@@ -389,7 +385,7 @@ int main (int argc, char *argv[])
int digits = 0; /* getopt() no. of float digits */
int nPvs; /* Number of PVs */
pv* pvs = 0; /* Array of PV structures */
pv* pvs; /* Array of PV structures */
LINE_BUFFER(stdout); /* Configure stdout buffering */
@@ -529,7 +525,7 @@ int main (int argc, char *argv[])
result = ca_context_create(ca_disable_preemptive_callback);
if (result != ECA_NORMAL) {
fprintf(stderr, "CA error %s occurred while trying "
"to start channel access '%s'.\n", ca_message(result), pvs[n].name);
"to start channel access.\n", ca_message(result));
return 1;
}
/* Allocate PV structure array */

View File

@@ -127,13 +127,13 @@ int cainfo (pv *pvs, int nPvs)
int main (int argc, char *argv[])
{
int n = 0;
int n;
int result; /* CA result */
int opt; /* getopt() current option */
int nPvs; /* Number of PVs */
pv* pvs = 0; /* Array of PV structures */
pv* pvs; /* Array of PV structures */
LINE_BUFFER(stdout); /* Configure stdout buffering */
@@ -195,7 +195,7 @@ int main (int argc, char *argv[])
result = ca_context_create(ca_disable_preemptive_callback);
if (result != ECA_NORMAL) {
fprintf(stderr, "CA error %s occurred while trying "
"to start channel access '%s'.\n", ca_message(result), pvs[n].name);
"to start channel access.\n", ca_message(result));
return 1;
}
/* Allocate PV structure array */

View File

@@ -92,7 +92,7 @@ void usage (void)
* Function: event_handler
*
* Description: CA event_handler for request type callback
* Allocates the dbr structure and copies the data
* Prints the event data
*
* Arg(s) In: args - event handler args (see CA manual)
*
@@ -107,10 +107,12 @@ static void event_handler (evargs args)
{
pv->dbrType = args.type;
pv->nElems = args.count;
memcpy(pv->value, args.dbr, dbr_size_n(args.type, args.count));
pv->value = (void *) args.dbr; /* casting away const */
print_time_val_sts(pv, reqElems);
fflush(stdout);
pv->value = NULL;
}
}
@@ -129,51 +131,39 @@ static void connection_handler ( struct connection_handler_args args )
{
pv *ppv = ( pv * ) ca_puser ( args.chid );
if ( args.op == CA_OP_CONN_UP ) {
nConn++;
if (!ppv->onceConnected) {
ppv->onceConnected = 1;
/* Set up pv structure */
/* ------------------- */
/* Get natural type and array count */
ppv->nElems = ca_element_count(ppv->chid);
ppv->dbfType = ca_field_type(ppv->chid);
ppv->dbfType = ca_field_type(ppv->chid);
ppv->dbrType = dbf_type_to_DBR_TIME(ppv->dbfType); /* Use native type */
if (dbr_type_is_ENUM(ppv->dbrType)) /* Enums honour -n option */
{
if (enumAsNr) ppv->dbrType = DBR_TIME_INT;
else ppv->dbrType = DBR_TIME_STRING;
}
else if (floatAsString &&
(dbr_type_is_FLOAT(ppv->dbrType) || dbr_type_is_DOUBLE(ppv->dbrType)))
{
ppv->dbrType = DBR_TIME_STRING;
}
/* Set request count */
ppv->nElems = ca_element_count(ppv->chid);
ppv->reqElems = reqElems > ppv->nElems ? ppv->nElems : reqElems;
/* Set up value structures */
ppv->dbrType = dbf_type_to_DBR_TIME(ppv->dbfType); /* Use native type */
if (dbr_type_is_ENUM(ppv->dbrType)) /* Enums honour -n option */
{
if (enumAsNr) ppv->dbrType = DBR_TIME_INT;
else ppv->dbrType = DBR_TIME_STRING;
}
else if (floatAsString &&
(dbr_type_is_FLOAT(ppv->dbrType) || dbr_type_is_DOUBLE(ppv->dbrType)))
{
ppv->dbrType = DBR_TIME_STRING;
}
/* Adjust array count */
if (reqElems > ppv->nElems)
reqElems = ppv->nElems;
ppv->reqElems = reqElems;
ppv->onceConnected = 1;
nConn++;
/* Issue CA request */
/* ---------------- */
/* install monitor once with first connect */
if ( ! ppv->value ) {
/* Allocate value structure */
ppv->value = calloc(1, dbr_size_n(ppv->dbrType, ppv->nElems));
if ( ppv->value ) {
ppv->status = ca_create_subscription(ppv->dbrType,
/* install monitor once with first connect */
ppv->status = ca_create_subscription(ppv->dbrType,
ppv->reqElems,
ppv->chid,
eventMask,
event_handler,
(void*)ppv,
NULL);
if ( ppv->status != ECA_NORMAL ) {
free ( ppv->value );
}
}
}
}
else if ( args.op == CA_OP_CONN_DOWN ) {
@@ -203,7 +193,7 @@ static void connection_handler ( struct connection_handler_args args )
int main (int argc, char *argv[])
{
int returncode = 0;
int n = 0;
int n;
int result; /* CA result */
IntFormatT outType; /* Output type */
@@ -211,7 +201,7 @@ int main (int argc, char *argv[])
int digits = 0; /* getopt() no. of float digits */
int nPvs; /* Number of PVs */
pv* pvs = 0; /* Array of PV structures */
pv* pvs; /* Array of PV structures */
LINE_BUFFER(stdout); /* Configure stdout buffering */
@@ -356,7 +346,7 @@ int main (int argc, char *argv[])
result = ca_context_create(ca_disable_preemptive_callback);
if (result != ECA_NORMAL) {
fprintf(stderr, "CA error %s occurred while trying "
"to start channel access '%s'.\n", ca_message(result), pvs[n].name);
"to start channel access.\n", ca_message(result));
return 1;
}
/* Allocate PV structure array */

View File

@@ -251,7 +251,7 @@ int caget (pv *pvs, int nPvs, OutputT format,
int main (int argc, char *argv[])
{
int n = 0;
int n;
int i;
int result; /* CA result */
OutputT format = plain; /* User specified format */
@@ -273,7 +273,7 @@ int main (int argc, char *argv[])
struct dbr_gr_enum bufGrEnum;
int nPvs; /* Number of PVs */
pv* pvs = 0; /* Array of PV structures */
pv* pvs; /* Array of PV structures */
LINE_BUFFER(stdout); /* Configure stdout buffering */
putenv("POSIXLY_CORRECT="); /* Behave correct on GNU getopt systems */
@@ -372,7 +372,7 @@ int main (int argc, char *argv[])
result = ca_context_create(ca_enable_preemptive_callback);
if (result != ECA_NORMAL) {
fprintf(stderr, "CA error %s occurred while trying "
"to start channel access '%s'.\n", ca_message(result), pvs[n].name);
"to start channel access.\n", ca_message(result));
return 1;
}
/* Allocate PV structure array */

View File

@@ -203,35 +203,44 @@ int epicsShareAPI dbel ( const char *pname, unsigned level )
}
if ( level > 1 ) {
unsigned nEntriesFree = ringSpace ( pevent->ev_que );
unsigned nEntriesFree;
const void * taskId;
LOCKEVQUE(pevent->ev_que);
nEntriesFree = ringSpace ( pevent->ev_que );
taskId = ( void * ) pevent->ev_que->evUser->taskid;
UNLOCKEVQUE(pevent->ev_que);
if ( nEntriesFree == 0u ) {
printf ( ", thread=%p, queue full",
(void *) pevent->ev_que->evUser->taskid );
(void *) taskId );
}
else if ( nEntriesFree == EVENTQUESIZE ) {
printf ( ", thread=%p, queue empty",
(void *) pevent->ev_que->evUser->taskid );
(void *) taskId );
}
else {
printf ( ", thread=%p, unused entries=%u",
(void *) pevent->ev_que->evUser->taskid, nEntriesFree );
(void *) taskId, nEntriesFree );
}
}
if ( level > 2 ) {
unsigned nDuplicates;
unsigned nCanceled;
if ( pevent->nreplace ) {
printf (", discarded by replacement=%ld", pevent->nreplace);
}
if ( ! pevent->valque ) {
printf (", queueing disabled" );
}
if ( pevent->ev_que->nDuplicates ) {
printf (", duplicate count =%u\n",
pevent->ev_que->nDuplicates );
LOCKEVQUE(pevent->ev_que);
nDuplicates = pevent->ev_que->nDuplicates;
nCanceled = pevent->ev_que->nCanceled;
UNLOCKEVQUE(pevent->ev_que);
if ( nDuplicates ) {
printf (", duplicate count =%u\n", nDuplicates );
}
if ( pevent->ev_que->nCanceled ) {
printf (", canceled count =%u\n",
pevent->ev_que->nCanceled );
if ( nCanceled ) {
printf (", canceled count =%u\n", nCanceled );
}
}
@@ -264,7 +273,7 @@ int epicsShareAPI dbel ( const char *pname, unsigned level )
*/
dbEventCtx epicsShareAPI db_init_events (void)
{
struct event_user *evUser;
struct event_user * evUser;
if (!dbevEventUserFreeList) {
freeListInitPvt(&dbevEventUserFreeList,
@@ -326,7 +335,7 @@ dbEventCtx epicsShareAPI db_init_events (void)
*/
void epicsShareAPI db_close_events (dbEventCtx ctx)
{
struct event_user *evUser = (struct event_user *) ctx;
struct event_user * const evUser = (struct event_user *) ctx;
/*
* Exit not forced on event blocks for now - this is left to channel
@@ -336,13 +345,32 @@ void epicsShareAPI db_close_events (dbEventCtx ctx)
* NOTE: not deleting events before calling this routine could be
* hazardous to the system's health.
*/
epicsMutexMustLock ( evUser->lock );
evUser->pendexit = TRUE;
epicsMutexUnlock ( evUser->lock );
/* notify the waiting task */
epicsEventSignal(evUser->ppendsem);
}
/*
* create_ev_que()
*/
static struct event_que * create_ev_que ( struct event_user * const evUser )
{
struct event_que * const ev_que = (struct event_que *)
freeListCalloc ( dbevEventQueueFreeList );
if ( ! ev_que ) {
return NULL;
}
ev_que->writelock = epicsMutexCreate();
if ( ! ev_que->writelock ) {
freeListFree ( dbevEventQueueFreeList, ev_que );
return NULL;
}
ev_que->evUser = evUser;
return ev_que;
}
/*
* DB_ADD_EVENT()
*/
@@ -350,13 +378,9 @@ dbEventSubscription epicsShareAPI db_add_event (
dbEventCtx ctx, struct dbAddr *paddr,
EVENTFUNC *user_sub, void *user_arg, unsigned select)
{
struct event_user *evUser = (struct event_user *) ctx;
struct dbCommon *precord;
struct event_que *ev_que;
struct event_que *tmp_que;
struct evSubscrip *pevent;
precord = paddr->precord;
struct event_user * const evUser = (struct event_user *) ctx;
struct event_que * ev_que;
struct evSubscrip * pevent;
/*
* Don't add events which will not be triggered
@@ -365,38 +389,42 @@ dbEventSubscription epicsShareAPI db_add_event (
return NULL;
}
pevent = freeListCalloc (dbevEventBlockFreeList);
if (!pevent) {
pevent = freeListCalloc ( dbevEventBlockFreeList );
if ( ! pevent ) {
return NULL;
}
/* find an event que block with enough quota */
/* otherwise add a new one to the list */
ev_que = &evUser->firstque;
while (TRUE) {
if (ev_que->quota + ev_que->nCanceled < EVENTQUESIZE - EVENTENTRIES) {
epicsMutexMustLock ( evUser->lock );
ev_que = & evUser->firstque;
while ( TRUE ) {
int success = 0;
LOCKEVQUE ( ev_que );
success = ( ev_que->quota + ev_que->nCanceled <
EVENTQUESIZE - EVENTENTRIES );
if ( success ) {
ev_que->quota += EVENTENTRIES;
}
UNLOCKEVQUE ( ev_que );
if ( success ) {
break;
}
if (!ev_que->nextque) {
tmp_que = (struct event_que *)
freeListCalloc(dbevEventQueueFreeList);
if (!tmp_que) {
freeListFree (dbevEventBlockFreeList, pevent);
return NULL;
if ( ! ev_que->nextque ) {
ev_que->nextque = create_ev_que ( evUser );
if ( ! ev_que->nextque ) {
ev_que = NULL;
break;
}
tmp_que->evUser = evUser;
tmp_que->writelock = epicsMutexCreate();
if (!tmp_que->writelock) {
freeListFree (dbevEventBlockFreeList, pevent);
freeListFree (dbevEventQueueFreeList, tmp_que);
return NULL;
}
ev_que->nextque = tmp_que;
ev_que = tmp_que;
break;
}
ev_que = ev_que->nextque;
}
epicsMutexUnlock ( evUser->lock );
if ( ! ev_que ) {
freeListFree ( dbevEventBlockFreeList, pevent );
return NULL;
}
pevent->npend = 0ul;
pevent->nreplace = 0ul;
@@ -409,10 +437,6 @@ dbEventSubscription epicsShareAPI db_add_event (
pevent->enabled = FALSE;
pevent->ev_que = ev_que;
LOCKEVQUE(ev_que);
ev_que->quota += EVENTENTRIES;
UNLOCKEVQUE(ev_que);
/*
* Simple types values queued up for reliable interprocess
* communication (for other types they get whatever happens to be
@@ -434,10 +458,9 @@ dbEventSubscription epicsShareAPI db_add_event (
*/
void epicsShareAPI db_event_enable (dbEventSubscription es)
{
struct evSubscrip *pevent = (struct evSubscrip *) es;
struct dbCommon *precord;
precord = (struct dbCommon *) pevent->paddr->precord;
struct evSubscrip * const pevent = (struct evSubscrip *) es;
struct dbCommon * const precord =
(struct dbCommon *) pevent->paddr->precord;
LOCKREC(precord);
if ( ! pevent->enabled ) {
@@ -452,10 +475,9 @@ void epicsShareAPI db_event_enable (dbEventSubscription es)
*/
void epicsShareAPI db_event_disable (dbEventSubscription es)
{
struct evSubscrip *pevent = (struct evSubscrip *) es;
struct dbCommon *precord;
precord = (struct dbCommon *) pevent->paddr->precord;
struct evSubscrip * const pevent = (struct evSubscrip *) es;
struct dbCommon * const precord =
(struct dbCommon *) pevent->paddr->precord;
LOCKREC(precord);
if ( pevent->enabled ) {
@@ -472,7 +494,7 @@ void epicsShareAPI db_event_disable (dbEventSubscription es)
static void event_remove ( struct event_que *ev_que,
unsigned short index, struct evSubscrip *placeHolder )
{
struct evSubscrip *pEvent = ev_que->evque[index];
struct evSubscrip * const pEvent = ev_que->evque[index];
ev_que->evque[index] = placeHolder;
if ( pEvent->npend == 1u ) {
@@ -495,12 +517,9 @@ static void event_remove ( struct event_que *ev_que,
*/
void epicsShareAPI db_cancel_event (dbEventSubscription es)
{
struct evSubscrip * pevent = ( struct evSubscrip * ) es;
struct dbCommon * precord;
struct evSubscrip * const pevent = ( struct evSubscrip * ) es;
unsigned short getix;
precord = ( struct dbCommon * ) pevent->paddr->precord;
db_event_disable ( es );
/*
@@ -564,12 +583,12 @@ void epicsShareAPI db_cancel_event (dbEventSubscription es)
*/
void epicsShareAPI db_flush_extra_labor_event (dbEventCtx ctx)
{
struct event_user *evUser = (struct event_user *) ctx;
struct event_user * const evUser = (struct event_user *) ctx;
epicsMutexMustLock ( evUser->lock );
while ( evUser->extraLaborBusy ) {
epicsMutexUnlock ( evUser->lock );
epicsThreadSleep(1.0);
epicsThreadSleep(0.1);
epicsMutexMustLock ( evUser->lock );
}
epicsMutexUnlock ( evUser->lock );
@@ -585,7 +604,7 @@ void epicsShareAPI db_flush_extra_labor_event (dbEventCtx ctx)
int epicsShareAPI db_add_extra_labor_event (
dbEventCtx ctx, EXTRALABORFUNC *func, void *arg)
{
struct event_user *evUser = (struct event_user *) ctx;
struct event_user * const evUser = (struct event_user *) ctx;
epicsMutexMustLock ( evUser->lock );
evUser->extralabor_sub = func;
@@ -600,7 +619,7 @@ int epicsShareAPI db_add_extra_labor_event (
*/
int epicsShareAPI db_post_extra_labor (dbEventCtx ctx)
{
struct event_user *evUser = (struct event_user *) ctx;
struct event_user * const evUser = (struct event_user *) ctx;
int doit;
epicsMutexMustLock ( evUser->lock );
@@ -627,12 +646,10 @@ int epicsShareAPI db_post_extra_labor (dbEventCtx ctx)
*/
static void db_post_single_event_private (struct evSubscrip *event)
{
struct event_que *ev_que;
db_field_log *pLog;
int firstEventFlag;
unsigned rngSpace;
ev_que = event->ev_que;
struct event_que * const ev_que = event->ev_que;
db_field_log * pLog;
int firstEventFlag;
unsigned rngSpace;
/*
* evUser ring buffer must be locked for the multiple
@@ -747,10 +764,10 @@ void *pField,
unsigned int caEventMask
)
{
struct dbCommon *pdbc = (struct dbCommon *)pRecord;
struct evSubscrip *event;
struct dbCommon * const pdbc = (struct dbCommon *)pRecord;
struct evSubscrip * event;
if (pdbc->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */
if (pdbc->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */
LOCKREC(pdbc);
@@ -762,8 +779,7 @@ unsigned int caEventMask
* changed or pval==NULL and waiting on alarms and alarms changed
*/
if ( (event->paddr->pfield == (void *)pField || pField==NULL) &&
(caEventMask & event->select)) {
(caEventMask & event->select) ) {
db_post_single_event_private (event);
}
}
@@ -778,8 +794,8 @@ unsigned int caEventMask
*/
void epicsShareAPI db_post_single_event (dbEventSubscription es)
{
struct evSubscrip *event = (struct evSubscrip *) es;
struct dbCommon *precord = event->paddr->precord;
struct evSubscrip * const event = (struct evSubscrip *) es;
struct dbCommon * const precord = event->paddr->precord;
dbScanLock (precord);
db_post_single_event_private (event);
@@ -898,8 +914,8 @@ static int event_read ( struct event_que *ev_que )
*/
static void event_task (void *pParm)
{
struct event_user *evUser = (struct event_user *) pParm;
struct event_que *ev_que;
struct event_user * const evUser = (struct event_user *) pParm;
struct event_que * ev_que;
/* init hook */
if (evUser->init_func) {
@@ -934,12 +950,14 @@ static void event_task (void *pParm)
epicsMutexMustLock ( evUser->lock );
}
evUser->extraLaborBusy = FALSE;
epicsMutexUnlock ( evUser->lock );
for ( ev_que = &evUser->firstque; ev_que;
ev_que = ev_que->nextque ) {
epicsMutexUnlock ( evUser->lock );
event_read (ev_que);
epicsMutexMustLock ( evUser->lock );
}
epicsMutexUnlock ( evUser->lock );
} while( ! evUser->pendexit );
@@ -975,7 +993,7 @@ int epicsShareAPI db_start_events (
dbEventCtx ctx,const char *taskname, void (*init_func)(void *),
void *init_func_arg, unsigned osiPriority )
{
struct event_user *evUser = (struct event_user *) ctx;
struct event_user * const evUser = (struct event_user *) ctx;
epicsMutexMustLock ( evUser->lock );
@@ -995,7 +1013,8 @@ int epicsShareAPI db_start_events (
taskname = EVENT_PEND_NAME;
}
evUser->taskid = epicsThreadCreate (
taskname, osiPriority, epicsThreadGetStackSize(epicsThreadStackMedium),
taskname, osiPriority,
epicsThreadGetStackSize(epicsThreadStackMedium),
event_task, (void *)evUser);
if (!evUser->taskid) {
epicsMutexUnlock ( evUser->lock );
@@ -1008,9 +1027,10 @@ int epicsShareAPI db_start_events (
/*
* db_event_change_priority()
*/
void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority )
void epicsShareAPI db_event_change_priority ( dbEventCtx ctx,
unsigned epicsPriority )
{
struct event_user * evUser = ( struct event_user * ) ctx;
struct event_user * const evUser = ( struct event_user * ) ctx;
epicsThreadSetPriority ( evUser->taskid, epicsPriority );
}
@@ -1019,9 +1039,11 @@ void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, unsigned epicsPrio
*/
void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx)
{
struct event_user *evUser = (struct event_user *) ctx;
struct event_user * const evUser = (struct event_user *) ctx;
epicsMutexMustLock ( evUser->lock );
evUser->flowCtrlMode = TRUE;
epicsMutexUnlock ( evUser->lock );
/*
* notify the event handler task
*/
@@ -1036,9 +1058,11 @@ void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx)
*/
void epicsShareAPI db_event_flow_ctrl_mode_off (dbEventCtx ctx)
{
struct event_user *evUser = (struct event_user *) ctx;
struct event_user * const evUser = (struct event_user *) ctx;
epicsMutexMustLock ( evUser->lock );
evUser->flowCtrlMode = FALSE;
epicsMutexUnlock ( evUser->lock );
/*
* notify the event handler task
*/

View File

@@ -74,7 +74,7 @@ void dbPutNotifyBlocker::cancel (
epicsGuard < epicsMutex > & guard )
{
guard.assertIdenticalMutex ( this->mutex );
if ( this->pn.paddr ) {
if ( this->pNotify ) {
epicsGuardRelease < epicsMutex > unguard ( guard );
dbNotifyCancel ( &this->pn );
}
@@ -100,26 +100,31 @@ void dbPutNotifyBlocker::expandValueBuf (
extern "C" void putNotifyCompletion ( putNotify *ppn )
{
dbPutNotifyBlocker * pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt );
{
epicsGuard < epicsMutex > guard ( pBlocker->mutex );
if ( pBlocker->pNotify ) {
if ( pBlocker->pn.status != putNotifyOK) {
pBlocker->pNotify->exception (
guard, ECA_PUTFAIL, "put notify unsuccessful",
static_cast <unsigned> (pBlocker->pn.dbrType),
static_cast <unsigned> (pBlocker->pn.nRequest) );
}
else {
pBlocker->pNotify->completion ( guard );
}
dbPutNotifyBlocker * const pBlocker =
static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt );
epicsGuard < epicsMutex > guard ( pBlocker->mutex );
cacWriteNotify * const pNtfy = pBlocker->pNotify;
if ( pNtfy ) {
pBlocker->pNotify = 0;
// Its necessary to signal the initiators now before we call
// the user callback. This is less efficent, and potentially
// causes more thread context switching, but its probably
// unavoidable because its possible that the use callback
// might destroy this object.
pBlocker->block.signal ();
if ( pBlocker->pn.status != putNotifyOK ) {
pNtfy->exception (
guard, ECA_PUTFAIL, "put notify unsuccessful",
static_cast < unsigned > (pBlocker->pn.dbrType),
static_cast < unsigned > (pBlocker->pn.nRequest) );
}
else {
errlogPrintf ( "put notify completion with nill pNotify?\n" );
pNtfy->completion ( guard );
}
pBlocker->pNotify = 0;
}
pBlocker->block.signal ();
else {
errlogPrintf ( "put notify completion with nill pNotify?\n" );
}
}
void dbPutNotifyBlocker::initiatePutNotify (

View File

@@ -44,6 +44,7 @@
#include "dbLock.h"
#include "devSup.h"
#include "drvSup.h"
#include "menuConvert.h"
#include "menuPini.h"
#include "registryRecordType.h"
#include "registryDeviceSupport.h"
@@ -68,6 +69,7 @@ static enum {
} iocState = iocVirgin;
/* define forward references*/
static int checkDatabase(dbBase *pdbbase);
static void initDrvSup(void);
static void initRecSup(void);
static void initDevSup(void);
@@ -98,8 +100,8 @@ int iocBuild(void)
}
errlogPrintf("Starting iocInit\n");
if (!pdbbase) {
errlogPrintf("iocBuild: Aborting, no database loaded!\n");
if (checkDatabase(pdbbase)) {
errlogPrintf("iocBuild: Aborting, bad database definition (DBD)!\n");
return -1;
}
epicsSignalInstallSigHupIgnore();
@@ -203,6 +205,83 @@ int iocPause(void)
return 0;
}
/*
* Database sanity checks
*
* This is not an attempt to sanity-check the whole .dbd file, only
* two menus normally get modified by users: menuConvert and menuScan.
*
* The menuConvert checks were added to flag problems with IOCs
* converted from 3.13.x, where the SLOPE choice didn't exist.
*
* The menuScan checks make sure the user didn't fiddle too much
* when creating new periodic scan choices.
*/
static int checkDatabase(dbBase *pdbbase)
{
const dbMenu *pMenu;
if (!pdbbase) {
errlogPrintf("checkDatabase: No database definitions loaded.\n");
return -1;
}
pMenu = dbFindMenu(pdbbase, "menuConvert");
if (!pMenu) {
errlogPrintf("checkDatabase: menuConvert not defined.\n");
return -1;
}
if (pMenu->nChoice <= menuConvertLINEAR) {
errlogPrintf("checkDatabase: menuConvert has too few choices.\n");
return -1;
}
if (strcmp(pMenu->papChoiceName[menuConvertNO_CONVERSION],
"menuConvertNO_CONVERSION")) {
errlogPrintf("checkDatabase: menuConvertNO_CONVERSION doesn't match.\n");
return -1;
}
if (strcmp(pMenu->papChoiceName[menuConvertSLOPE], "menuConvertSLOPE")) {
errlogPrintf("checkDatabase: menuConvertSLOPE doesn't match.\n");
return -1;
}
if (strcmp(pMenu->papChoiceName[menuConvertLINEAR], "menuConvertLINEAR")) {
errlogPrintf("checkDatabase: menuConvertLINEAR doesn't match.\n");
return -1;
}
pMenu = dbFindMenu(pdbbase, "menuScan");
if (!pMenu) {
errlogPrintf("checkDatabase: menuScan not defined.\n");
return -1;
}
if (pMenu->nChoice <= menuScanI_O_Intr) {
errlogPrintf("checkDatabase: menuScan has too few choices.\n");
return -1;
}
if (strcmp(pMenu->papChoiceName[menuScanPassive],
"menuScanPassive")) {
errlogPrintf("checkDatabase: menuScanPassive doesn't match.\n");
return -1;
}
if (strcmp(pMenu->papChoiceName[menuScanEvent],
"menuScanEvent")) {
errlogPrintf("checkDatabase: menuScanEvent doesn't match.\n");
return -1;
}
if (strcmp(pMenu->papChoiceName[menuScanI_O_Intr],
"menuScanI_O_Intr")) {
errlogPrintf("checkDatabase: menuScanI_O_Intr doesn't match.\n");
return -1;
}
if (pMenu->nChoice <= SCAN_1ST_PERIODIC) {
errlogPrintf("checkDatabase: menuScan has no periodic choices.\n");
return -1;
}
return 0;
}
static void initDrvSup(void) /* Locate all driver support entry tables */
{

View File

@@ -232,19 +232,30 @@ int min = sched_get_priority_min(prm->policy);
int max = sched_get_priority_max(prm->policy);
int low, try;
prm->min_pri = -1;
prm->max_pri = -1;
if ( -1 == min || -1 == max ) {
/* something is very wrong... */
/* something is very wrong; maintain old behavior
* (warning message if sched_get_priority_xxx() fails
* and use default policy's sched_priority [even if
* that is likely to cause epicsThreadCreate to fail
* because that priority is not suitable for SCHED_FIFO]).
*/
prm->min_pri = prm->max_pri = -1;
return 0;
}
if ( try_pri(min, prm->policy) ) {
/* cannot create thread at minimum priority;
* probably no permission to use SCHED_FIFO
* at all.
* at all. However, we still must return
* a priority range accepted by the SCHED_FIFO
* policy. Otherwise, epicsThreadCreate() cannot
* detect the unsufficient permission (EPERM)
* and fall back to a non-RT thread (because
* pthread_attr_setschedparam would fail with
* EINVAL due to the bad priority).
*/
prm->min_pri = prm->max_pri = min;
return 0;
}

View File

@@ -1,11 +1,10 @@
#!/usr/bin/perl
#*************************************************************************
# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
# Copyright (c) 2011 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.
# EPICS BASE Versions 3.13.7
# and higher are distributed subject to a Software License Agreement found
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
#
@@ -62,18 +61,13 @@ foreach $source ( @files )
$basename=$source;
$basename=~s'.*[/\\]'';
$target = "$install_dir/$basename";
$temp = "$target.$$";
# The Win32 filesystem seems to be 'slow',
# i.e. $target may look like 'up to date'
# unless you wait an hour.
# -> skip this test on WIN32 ?
#if (-f $target and $^O ne "MSWin32")
if (-f $target)
{
if (-M $target < -M $source and
if (-M $target < -M $source and
-C $target < -C $source)
{
print "$target is up to date\n";
next;
}
else
@@ -84,8 +78,9 @@ foreach $source ( @files )
}
}
# print "Installing $source into $install_dir\n";
copy ($source, $target) or die "Copy failed";
# Using copy + rename fixes problems with parallel builds:
copy ($source, $temp) or die "Copy failed: $!\n";
rename ($temp, $target) or die "Rename failed: $!\n";
# chmod 0555 <read-only> DOES work on WIN32, but:
# Another chmod 0777 to make it write- and deletable