Merge 7.0.10 into PSI-7.0

This commit is contained in:
2026-01-06 14:57:16 +01:00
347 changed files with 5707 additions and 2814 deletions
+376
View File
@@ -0,0 +1,376 @@
# IOC Access Security
## ACF Syntax Forward Compatibility
EPICS 7.0.10 modified the Access Security Configuration File (ACF) parser to
standardize the ACF grammar for forward compatibility.
It did not change the syntax that was accepted by earlier versions of the parser,
so existing access security configuration files would not need to be modified.
All ACF definitions will adhere to a consistent syntax format,
which will allow future additions to the access security language
without breaking existing configurations.
In practice, this means the structure of ACF files is now formally defined
and will remain stable going forward,
so any new grammar features will fit into the same pattern.
(Existing ACF files continue to work as-is under the new parser,
so no changes are required for legacy configurations or tools.).
**Generic ACF Syntax:**
The ACF file consists of definitions for User Access Groups (UAG),
Host Access Groups (HAG),
and Access Security Groups (ASG),
using the following general format
(angle brackets below denote placeholders):
```text
UAG(<name>) [{ <user> [, <user> ...] }]
...
HAG(<name>) [{ <host> [, <host> ...] }]
...
ASG(<name>) [{
[INP<index>(<pvname>)
...]
RULE(<level>, NONE | READ | WRITE [, NOTRAPWRITE | TRAPWRITE]) {
[UAG(<name> [, <name> ...])]
[HAG(<name> [, <name> ...])]
[CALC(<calculation>)]
}
...
}]
...
```
Under this schema each definition comprises a keyword,
a name in parentheses,
and (optionally) a braced block of contents.
This uniform structure ensures that
**future keywords or sections**
can be introduced in the same form,
maintaining compatibility with the parser.
For example, if a new type of condition or group is added in a later release,
it would follow the `KEYWORD(name) { ... }` pattern,
so 7.0.10-era parsers can handle or ignore it gracefully
instead of failing on unknown syntax.
**Supported Syntax in EPICS 7.0.10:**
The current release defines the following specific elements
within the above generic format:
- **UAG** -- *User Access Group*.
Defines a group of user names.
- **HAG** -- *Host Access Group*.
Defines a group of host names
(or IP addresses) that clients can connect from.
- **ASG** -- *Access Security Group*.
Defines a security group which records can be assigned to.
An ASG entry may contain a block with input definitions and access rules.
For example:
```text
ASG(MyGroup) {
INPA(myPV1)
INPB(myPV2)
RULE(1, WRITE) { ... }
RULE(1, READ) { ... }
}
```
If no rules are defined for an ASG,
the access permissions default to always allowed.
- **INP<index>(<pvname>)** -- *Input link*.
Declares an input process variable whose value can be used in a CALC condition.
- **RULE(<level>, <permission> [, <logOption>]) { ... }** --
Defines an access rule for the ASG.
Inside the curly braces of a RULE,
**optional conditions** can restrict when that rule applies.
All conditions that are present must be satisfied
(they function as a logical AND):
- **UAG(<name>, ...)** -- User-group condition.
The rule only applies if the Channel Access client's user
is a member of one of the listed UAGs.
- **HAG(<name>, ...)** -- Host-group condition.
The rule only applies if the client's host
(as determined by its IP or hostname) is in one of the listed HAGs
- **CALC("<expression>")** -- Calculation condition.
The rule only applies if the given expression evaluates to true (non-zero).
**Special Semantics for RULEs:**
Rules will continue to allow the prescribed access if and only if
all the predicates the rule contains are satisfied.
- If the rule contains predicates that are unknown to the parser
(indicating future functionality),
then the rule will NOT not match,
but no syntax error will be reported as long as the syntax is correct.
- If the rule contains predicates that the parser does not recognise
which are malformed (e.g. missing parentheses),
then the rule will not match and the parser will report a syntax error.
- In this way rules can be extended with new predicates
without breaking older clients or giving those older clients elevated privileges.
**Special Semantics for unrecognised ACF file elements:**
Any elements that are included in an ACF file will be ignored silently
by a parser that does not understand them.
- If an element is seen in an ACF file that is not understood by the parser,
the parser will simply ignore it silently,
without reporting an error,
as long as its syntax is correct.
- If elements are added to the ACF file that are malformed
(e.g. missing parentheses),
the parser will report a syntax error.
- Thus new elements can be added to ACF files in new EPICS releases
without breaking older clients that loads those files.
In summary, ACF forward compatibility means that from EPICS 7.0.10 onward,
any new access security features will use this established syntax.
The parser will recognize new group types or rule options using the same
`<KEYWORD>(...) { ... }` convention,
ensuring they can be used in files loaded by IOCs running EPICS 7.0.10 or later
without being rejected by those IOCs or requiring their parser to be modified.
This change does not require any modifications to existing ACF files --
all legacy syntax remains valid,
and the new standardized grammar provides a robust foundation for future extensions.
---
## Full Language Specification for Access Security Configuration Files
### Lexical tokens
**Ignored stuff**
- *Whitespace*: space, tab, `\r` (carriage return) -- may appear between tokens.
- *Newlines*: `\n` -- same as whitespace for syntax, but tracked for error messages.
- *Comments*: `#` ... end of line -- ignored.
**Terminals**
- `UAG` → literal string `"UAG"`
- `HAG``"HAG"`
- `ASG``"ASG"`
- `RULE``"RULE"`
- `CALC``"CALC"`
- `INP(link)` → literal `"INP"` followed immediately by one uppercase letter `A`-`U`
```text
link-letter ::= "A" | "B" | ... | "U"
INP(link) ::= "INP" link-letter
```
- `INT` → integer literal
```text
INT ::= [ "+" | "-" ]? DIGIT+
DIGIT ::= "0" | "1" | ... | "9"
```
- `FLOAT` → floating-point literal with decimal point
```text
FLOAT ::= [ "+" | "-" ]? DIGIT* "." DIGIT+ [ ( "e" | "E" ) [ "+" | "-" ] DIGIT+ ]?
```
- `STRING` → either
- **Unquoted**: One or more of
```text
NAMECHAR ::= letter | digit | "_" | "-" | "+" | ":" | "." | "[" | "]" | "<" | ">" | ";"
STRING(unquoted) ::= NAMECHAR+
```
- **Quoted**: Surrounding quotes are stripped;
escapes are kept literal at parse level.
```text
STRING(quoted) ::= '"' { STRINGCHAR | ESCAPE } '"'
STRINGCHAR ::= any char except '"' "\" "\n"
ESCAPE ::= "\" any-char
```
- Punctuation tokens (single-character terminals):
```text
"(" ")" "{" "}" ","
```
---
### Grammar
#### Top level
```ebnf
acf-file ::= asconfig ;
asconfig ::= asconfig-item { asconfig-item } ;
asconfig-item ::=
uag-def
| hag-def
| asg-def
| generic-top-level-item ;
```
##### UAG / HAG groups
```ebnf
uag-def ::= "UAG" uag-head [ uag-body ] ;
uag-head ::= "(" STRING ")" ;
uag-body ::= "{" uag-user-list "}" ;
uag-user-list ::= STRING { "," STRING } ;
```
```ebnf
hag-def ::= "HAG" hag-head [ hag-body ] ;
hag-head ::= "(" STRING ")" ;
hag-body ::= "{" hag-host-list "}" ;
hag-host-list ::= STRING { "," STRING } ;
```
##### ASG (access security group)
```ebnf
asg-def ::= "ASG" asg-head [ asg-body ] ;
asg-head ::= "(" STRING ")" ;
asg-body ::= "{" asg-body-item { asg-body-item } "}" ;
asg-body-item ::=
inp-config
| rule-config ;
```
###### INP config
```ebnf
inp-config ::= INP(link) "(" STRING ")" ;`
```
###### RULE config
```ebnf
rule-config ::= "RULE" rule-head [ rule-body ] ;
rule-head ::=
"(" rule-head-mandatory "," rule-log-option ")"
| "(" rule-head-mandatory ")" ;
rule-head-mandatory ::= INT "," STRING ;
rule-log-option ::= STRING ;
```
```ebnf
rule-body ::= "{" rule-list "}" ;
rule-list ::= rule-list-item { rule-list-item } ;
rule-list-item ::=
"UAG" "(" rule-uag-list ")"
| "HAG" "(" rule-hag-list ")"
| "CALC" "(" STRING ")"
| rule-generic-block-elem ;
```
```ebnf
rule-uag-list ::= STRING { "," STRING } ;
rule-hag-list ::= STRING { "," STRING } ;`
```
##### Generic / future-proof syntax
These are the "catch-all" constructs that are **parsed** but currently **ignored** semantically.
###### Keyword classes
These are parser-level categories used inside generic constructs:
```ebnf
keyword ::=
"UAG"
| "HAG"
| "CALC"
| non-rule-keyword ;
non-rule-keyword ::=
"ASG"
| "RULE"
| INP(link) ; (* INPA..INPU *)
```
###### Generic head (argument list)
```ebnf
generic-head ::=
"(" ")"
| "(" generic-element ")"
| "(" generic-list ")" ;
generic-list ::= generic-element "," generic-element { "," generic-element } ;
generic-element ::=
keyword
| STRING
| INT
| FLOAT ;
```
###### Generic blocks
```ebnf
generic-block ::=
"{" generic-element "}"
| "{" generic-list "}"
| "{" generic-block-list "}" ;
generic-block-list ::= generic-block-elem { generic-block-elem } ;
generic-block-elem ::=
generic-block-elem-name generic-head [ generic-block ] ;
generic-block-elem-name ::= keyword | STRING ;
```
###### Generic top-level items
These are "unknown" top-level constructs, all of which are parsed and then ignored with a warning.
```ebnf
generic-top-level-item ::=
STRING generic-head generic-list-block
| STRING generic-head generic-block
| STRING generic-head ;
```
where
```ebnf
generic-list-block ::=
"{" generic-element "}" "{" generic-list "}" ;
```
###### Generic blocks inside RULE bodies
These are the "future predicates" in a RULE's body; they cause the RULE to be disabled with a warning, but they **must** still parse.
```ebnf
rule-generic-block-elem ::=
rule-generic-block-elem-name generic-head [ generic-block ] ;
rule-generic-block-elem-name ::= non-rule-keyword | STRING ;
```
+2
View File
@@ -13,6 +13,8 @@
SRC_DIRS += $(LIBCOM)/as
DOCS += ACF-Language.md
INC += asLib.h
INC += asTrapWrite.h
+1
View File
@@ -202,6 +202,7 @@ typedef struct{
ELLLIST uagList; /*List of ASGUAG*/
ELLLIST hagList; /*List of ASGHAG*/
int trapMask;
int ignore; // 1 if rule to be ignored because of unknown elements
} ASGRULE;
typedef struct{
ELLNODE node;
+173 -36
View File
@@ -12,26 +12,44 @@ static int yyerror(char *);
static int yy_start;
#include "asLibRoutines.c"
static int yyFailed = FALSE;
static int yyWarned = FALSE;
static int line_num=1;
static UAG *yyUag=NULL;
static HAG *yyHag=NULL;
static ASG *yyAsg=NULL;
static ASGRULE *yyAsgRule=NULL;
static
char* yystrdup(const char *inp) {
char* ret = strdup(inp);
if(!ret)
yyerror("MALLOC");
return ret;
}
%}
%start asconfig
%token tokenUAG tokenHAG tokenASG tokenRULE tokenCALC
%token <Str> tokenINP
%token <Int> tokenINTEGER
%token <Str> tokenSTRING
%token <Int64> tokenINT64 tokenINP
%token <Float64> tokenFLOAT64
%union
{
int Int;
epicsInt64 Int64;
epicsFloat64 Float64;
char *Str;
}
%type <Str> non_rule_keyword
%type <Str> generic_block_elem_name
%type <Str> generic_block_elem
%type <Str> rule_generic_block_elem
%type <Str> rule_generic_block_elem_name
%type <Str> keyword
%%
asconfig: asconfig asconfig_item
@@ -43,13 +61,117 @@ asconfig_item: tokenUAG uag_head uag_body
| tokenHAG hag_head
| tokenASG asg_head asg_body
| tokenASG asg_head
| generic_item
;
/* uniformally yield a string for use in warning messages */
keyword: tokenUAG
{ $$ = yystrdup("UAG"); }
| tokenHAG
{ $$ = yystrdup("HAG"); }
| tokenCALC
{ $$ = yystrdup("CALC"); }
| non_rule_keyword
;
non_rule_keyword: tokenASG
{ $$ = yystrdup("ASG"); }
| tokenRULE
{ $$ = yystrdup("RULE"); }
| tokenINP
{
if(!!($$ = yystrdup("INPA")))
$$[3] += $1; /* 'A' + input number */
}
;
generic_item: tokenSTRING generic_head generic_list_block
{
yywarn("Ignoring unsupported TOP LEVEL nested block", $1);
free($1);
}
| tokenSTRING generic_head generic_block
{
yywarn("Ignoring unsupported TOP LEVEL block", $1);
free($1);
}
| tokenSTRING generic_head
{
yywarn("Ignoring unsupported TOP LEVEL bare block", $1);
free($1);
}
;
generic_head: '(' ')'
| '(' generic_element ')'
| '(' generic_list ')'
;
generic_list_block: '{' generic_element '}'
'{' generic_list '}'
;
generic_list: generic_list ',' generic_element
| generic_element ',' generic_element
;
generic_element: keyword
| tokenSTRING
{
free($1);
}
| tokenINT64
| tokenFLOAT64
;
generic_block: '{' generic_element '}'
| '{' generic_list '}'
| '{' generic_block_list '}'
;
generic_block_list: generic_block_list generic_block_elem
{
free($2);
}
| generic_block_elem
{
free($1);
}
;
generic_block_elem: generic_block_elem_name generic_head generic_block
{
$$ = $1;
}
| generic_block_elem_name generic_head
{
$$ = $1;
}
;
generic_block_elem_name: keyword
| tokenSTRING
;
rule_generic_block_elem: rule_generic_block_elem_name generic_head generic_block
{
$$ = $1;
}
| rule_generic_block_elem_name generic_head
{
$$ = $1;
}
;
rule_generic_block_elem_name: non_rule_keyword
| tokenSTRING
;
uag_head: '(' tokenSTRING ')'
{
yyUag = asUagAdd($2);
if(!yyUag) yyerror("");
free((void *)$2);
free($2);
}
;
@@ -67,7 +189,7 @@ uag_user_list_name: tokenSTRING
{
if (asUagAddUser(yyUag,$1))
yyerror("");
free((void *)$1);
free($1);
}
;
@@ -75,7 +197,7 @@ hag_head: '(' tokenSTRING ')'
{
yyHag = asHagAdd($2);
if(!yyHag) yyerror("");
free((void *)$2);
free($2);
}
;
@@ -90,7 +212,7 @@ hag_host_list_name: tokenSTRING
{
if (asHagAddHost(yyHag,$1))
yyerror("");
free((void *)$1);
free($1);
}
;
@@ -98,7 +220,7 @@ asg_head: '(' tokenSTRING ')'
{
yyAsg = asAsgAdd($2);
if(!yyAsg) yyerror("");
free((void *)$2);
free($2);
}
;
@@ -114,49 +236,49 @@ asg_body_item: inp_config | rule_config
inp_config: tokenINP '(' tokenSTRING ')'
{
if (asAsgAddInp(yyAsg,$3,$<Int>1))
if (asAsgAddInp(yyAsg,$3,(int)$<Int64>1))
yyerror("");
free((void *)$3);
free($3);
}
;
rule_config: tokenRULE rule_head rule_body
| tokenRULE rule_head
rule_head: rule_head_manditory rule_head_options
rule_head: '(' rule_head_mandatory ',' rule_log_option ')'
| '(' rule_head_mandatory ')'
;
rule_head_manditory: '(' tokenINTEGER ',' tokenSTRING
rule_head_mandatory: tokenINT64 ',' tokenSTRING
{
asAccessRights rights;
if((strcmp($4,"NONE")==0)) {
rights=asNOACCESS;
} else if((strcmp($4,"READ")==0)) {
rights=asREAD;
} else if((strcmp($4,"WRITE")==0)) {
rights=asWRITE;
if ($1 < 0) {
char message[60];
sprintf(message, "RULE: LEVEL must be positive: %lld", $1);
yyerror(message);
} else if((strcmp($3,"NONE")==0)) {
yyAsgRule = asAsgAddRule(yyAsg,asNOACCESS,(int)$1);
} else if((strcmp($3,"READ")==0)) {
yyAsgRule = asAsgAddRule(yyAsg,asREAD,(int)$1);
} else if((strcmp($3,"WRITE")==0)) {
yyAsgRule = asAsgAddRule(yyAsg,asWRITE,(int)$1);
} else {
yyerror("Access rights must be NONE, READ or WRITE");
rights = asNOACCESS;
yywarn("Ignoring RULE that contains an unsupported keyword", $3);
}
yyAsgRule = asAsgAddRule(yyAsg,rights,$2);
free((void *)$4);
free($3);
}
;
rule_head_options: ')'
| rule_log_options
rule_log_options: ',' tokenSTRING ')'
rule_log_option: tokenSTRING
{
if((strcmp($2,"TRAPWRITE")==0)) {
if((strcmp($1,"TRAPWRITE")==0)) {
long status;
status = asAsgAddRuleOptions(yyAsgRule,AS_TRAP_WRITE);
if(status) yyerror("");
} else if((strcmp($2,"NOTRAPWRITE")!=0)) {
} else if((strcmp($1,"NOTRAPWRITE")!=0)) {
yyerror("Log options must be TRAPWRITE or NOTRAPWRITE");
}
free((void *)$2);
free($1);
}
;
@@ -173,7 +295,14 @@ rule_list_item: tokenUAG '(' rule_uag_list ')'
{
if (asAsgRuleCalc(yyAsgRule,$3))
yyerror("");
free((void *)$3);
free($3);
}
| rule_generic_block_elem
{
yywarn("Ignoring RULE that contains an unsupported keyword", $1);
free($1);
if (asAsgRuleDisable(yyAsgRule))
yyerror("");
}
;
@@ -185,7 +314,7 @@ rule_uag_list_name: tokenSTRING
{
if (asAsgRuleUagAdd(yyAsgRule,$1))
yyerror("");
free((void *)$1);
free($1);
}
;
@@ -197,7 +326,7 @@ rule_hag_list_name: tokenSTRING
{
if (asAsgRuleHagAdd(yyAsgRule,$1))
yyerror("");
free((void *)$1);
free($1);
}
;
%%
@@ -207,12 +336,19 @@ rule_hag_list_name: tokenSTRING
static int yyerror(char *str)
{
if (strlen(str))
errlogPrintf("%s at line %d\n", str, line_num);
fprintf(stderr, ERL_ERROR " %s at line %d\n", str, line_num);
else
errlogPrintf(ERL_ERROR " at line %d\n", line_num);
fprintf(stderr, ERL_ERROR " at line %d\n", line_num);
yyFailed = TRUE;
return 0;
}
static int yywarn(char *str, char *token)
{
if (!yyWarned && strlen(str) && strlen(token))
fprintf(stderr, ERL_WARNING " %s at line %d: %s\n", str, line_num, token);
yyWarned = TRUE;
return 0;
}
static int myParse(ASINPUTFUNCPTR inputfunction)
{
static int FirstFlag = 1;
@@ -222,6 +358,7 @@ static int myParse(ASINPUTFUNCPTR inputfunction)
if (!FirstFlag) {
line_num=1;
yyFailed = FALSE;
yyWarned = FALSE;
yyreset();
yyrestart(NULL);
}
+33 -13
View File
@@ -66,6 +66,7 @@ static long asAsgAddRuleOptions(ASGRULE *pasgrule,int trapMask);
static long asAsgRuleUagAdd(ASGRULE *pasgrule,const char *name);
static long asAsgRuleHagAdd(ASGRULE *pasgrule,const char *name);
static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc);
static long asAsgRuleDisable(ASGRULE *pasgrule);
/*
asInitialize can be called while access security is already active.
@@ -96,7 +97,7 @@ long epicsStdCall asInitialize(ASINPUTFUNCPTR inputfunction)
HAGNAME *phagname;
static epicsThreadOnceId asInitializeOnceFlag = EPICS_THREAD_ONCE_INIT;
epicsThreadOnce(&asInitializeOnceFlag,asInitializeOnce,(void *)0);
epicsThreadOnce(&asInitializeOnceFlag,asInitializeOnce,NULL);
LOCK;
pasbasenew = asCalloc(1,sizeof(ASBASE));
if(!freeListPvt) freeListInitPvt(&freeListPvt,sizeof(ASGCLIENT),20);
@@ -585,8 +586,8 @@ int epicsStdCall asDumpFP(
pasginp = (ASGINP *)ellNext(&pasginp->node);
}
while(pasgrule) {
int print_end_brace;
int print_rule_end_brace = FALSE;
if (pasgrule->ignore) goto next_rule;
fprintf(fp,"\tRULE(%d,%s,%s)",
pasgrule->level,asAccessName[pasgrule->access],
asTrapOption[pasgrule->trapMask]);
@@ -594,10 +595,10 @@ int epicsStdCall asDumpFP(
pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList);
if(pasguag || pasghag || pasgrule->calc) {
fprintf(fp," {\n");
print_end_brace = TRUE;
print_rule_end_brace = TRUE;
} else {
fprintf(fp,"\n");
print_end_brace = FALSE;
print_rule_end_brace = FALSE;
}
if(pasguag) fprintf(fp,"\t\tUAG(");
while(pasguag) {
@@ -605,7 +606,6 @@ int epicsStdCall asDumpFP(
pasguag = (ASGUAG *)ellNext(&pasguag->node);
if(pasguag) fprintf(fp,","); else fprintf(fp,")\n");
}
pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList);
if(pasghag) fprintf(fp,"\t\tHAG(");
while(pasghag) {
fprintf(fp,"%s",pasghag->phag->name);
@@ -618,7 +618,8 @@ int epicsStdCall asDumpFP(
fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE"));
fprintf(fp,"\n");
}
if(print_end_brace) fprintf(fp,"\t}\n");
next_rule:
if(print_rule_end_brace) fprintf(fp,"\t}\n");
pasgrule = (ASGRULE *)ellNext(&pasgrule->node);
}
pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList);
@@ -735,7 +736,7 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname)
pasg = (ASG *)ellFirst(&pasbase->asgList);
if(!pasg) fprintf(fp,"No ASGs\n");
while(pasg) {
int print_end_brace;
int print_end_brace = FALSE;
if(asgname && strcmp(asgname,pasg->name)!=0) {
pasg = (ASG *)ellNext(&pasg->node);
@@ -761,8 +762,8 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname)
pasginp = (ASGINP *)ellNext(&pasginp->node);
}
while(pasgrule) {
int print_end_brace;
int print_rule_end_brace = FALSE;
if ( pasgrule->ignore) goto next_rule;
fprintf(fp,"\tRULE(%d,%s,%s)",
pasgrule->level,asAccessName[pasgrule->access],
asTrapOption[pasgrule->trapMask]);
@@ -770,10 +771,10 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname)
pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList);
if(pasguag || pasghag || pasgrule->calc) {
fprintf(fp," {\n");
print_end_brace = TRUE;
print_rule_end_brace = TRUE;
} else {
fprintf(fp,"\n");
print_end_brace = FALSE;
print_rule_end_brace = FALSE;
}
if(pasguag) fprintf(fp,"\t\tUAG(");
while(pasguag) {
@@ -793,7 +794,8 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname)
fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE"));
fprintf(fp,"\n");
}
if(print_end_brace) fprintf(fp,"\t}\n");
next_rule:
if(print_rule_end_brace) fprintf(fp,"\t}\n");
pasgrule = (ASGRULE *)ellNext(&pasgrule->node);
}
if(print_end_brace) fprintf(fp,"}\n");
@@ -948,6 +950,7 @@ static long asComputeAsgPvt(ASG *pasg)
if(!asActive) return(S_asLib_asNotActive);
pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList);
while(pasgrule) {
if ( pasgrule->ignore) goto next_rule;
double result = pasgrule->result; /* set for VAL */
long status;
@@ -960,6 +963,8 @@ static long asComputeAsgPvt(ASG *pasg)
pasgrule->result = ((result>.99) && (result<1.01)) ? 1 : 0;
}
}
next_rule:
pasgrule = (ASGRULE *)ellNext(&pasgrule->node);
}
pasg->inpChanged = FALSE;
@@ -995,6 +1000,7 @@ static long asComputePvt(ASCLIENTPVT asClientPvt)
oldaccess=pasgclient->access;
pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList);
while(pasgrule) {
if (pasgrule->ignore) goto next_rule;
if(access == asWRITE) break;
if(access>=pasgrule->access) goto next_rule;
if(pasgclient->level > pasgrule->level) goto next_rule;
@@ -1059,6 +1065,9 @@ void asFreeAll(ASBASE *pasbase)
ASGUAG *pasguag;
void *pnext;
if(!pasbase)
return;
puag = (UAG *)ellFirst(&pasbase->uagList);
while(puag) {
puagname = (UAGNAME *)ellFirst(&puag->list);
@@ -1408,3 +1417,14 @@ static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc)
}
return(status);
}
/**
* @brief Disable a rule if it contains unsupported elements
* @param pasgrule the rule to disable
* @return Non-zero if rule was not disabled
*/
static long asAsgRuleDisable(ASGRULE *pasgrule) {
if (!pasgrule) return 1;
pasgrule->ignore = 1;
return 0;
}
+25 -6
View File
@@ -21,6 +21,8 @@ punctuation [(){},]
link [A-U]
%{
#include "epicsStdlib.h"
static ASINPUTFUNCPTR *my_yyinput;
#undef YY_INPUT
#define YY_INPUT(b,r,ms) (r=(*my_yyinput)((char *)b,ms))
@@ -43,14 +45,31 @@ RULE { return(tokenRULE); }
CALC { return(tokenCALC); }
INP{link} {
yylval.Int = (unsigned char)yytext[3];
yylval.Int -= 'A';
yylval.Int64 = (unsigned char)yytext[3];
yylval.Int64 -= 'A';
return(tokenINP);
}
{digit}+ { /*integer*/
yylval.Int = atoi((char *)yytext);
return(tokenINTEGER);
[-+]?{digit}*\.{digit}+([eE][-+]?{digit}+)? {
char *end;
if (epicsParseDouble((char *)yytext, &yylval.Float64, &end) ) {
char message[40];
sprintf(message, "Error parsing Float64: %s", (char *)yytext);
yyerror(message);
} else {
return(tokenFLOAT64);
}
}
[-+]?{digit}+ { /*integer 64*/
char *end;
if (epicsParseInt64((char *)yytext, &yylval.Int64, 10, &end) ) {
char message[40];
sprintf(message, "Error parsing Int64: %s", (char *)yytext);
yyerror(message);
} else {
return(tokenINT64);
}
}
{name}+ { /*unquoted string*/
@@ -60,7 +79,7 @@ INP{link} {
{doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */
yylval.Str=asStrdup(yytext+1);
yylval.Str[strlen(yylval.Str)-1] = '\0';
yylval.Str[strlen(yylval.Str)-1] = '\0'; /* overwrite trailing '"' */
return(tokenSTRING);
}
+1 -1
View File
@@ -40,7 +40,7 @@ static int cond_search(const char **ppinst, int match);
/* calcPerform
*
* Evalutate the postfix expression
* Evaluate the postfix expression
*/
LIBCOM_API long
calcPerform(double *parg, double *presult, const char *pinst)
+3 -3
View File
@@ -42,7 +42,7 @@ typedef enum {
UNARY_OPERATOR,
VARARG_OPERATOR,
BINARY_OPERATOR,
SEPERATOR,
SEPARATOR,
CLOSE_PAREN,
CONDITIONAL,
EXPR_TERMINATOR,
@@ -155,7 +155,7 @@ static const ELEMENT operators[] = {
{"*", 5, 5, -1, BINARY_OPERATOR,MULT},
{"**", 6, 6, -1, BINARY_OPERATOR,POWER},
{"+", 4, 4, -1, BINARY_OPERATOR,ADD},
{",", 0, 0, 0, SEPERATOR, NOT_GENERATED},
{",", 0, 0, 0, SEPARATOR, NOT_GENERATED},
{"-", 4, 4, -1, BINARY_OPERATOR,SUB},
{"/", 5, 5, -1, BINARY_OPERATOR,DIV},
{":", 0, 0, -1, CONDITIONAL, COND_ELSE},
@@ -344,7 +344,7 @@ LIBCOM_API long
operand_needed = TRUE;
break;
case SEPERATOR:
case SEPARATOR:
if (pstacktop == stack) {
*perror = CALC_ERR_BAD_SEPERATOR;
goto bad;
+185 -168
View File
@@ -22,7 +22,10 @@
#include "libComAPI.h"
/** \brief Number of input arguments to a calc expression (A-U) */
/** \brief Number of input arguments to a calc expression (A-U)
*
* Since 7.0.10 the number of inputs has been increased from 12 to 21.
*/
#define CALCPERFORM_NARGS 21
/** \brief Size of the internal partial result stack */
#define CALCPERFORM_STACK 80
@@ -41,20 +44,18 @@
* few bytes smaller for some sizes.
*
* The maximum expansion from infix to postfix is for the sub-expression
\code
.1?.1:
\endcode
* which is 6 characters long and results in 21 bytes of postfix:
\code
* <tt>.1?.1:</tt> which is 6 characters long and results in 21 bytes of
* postfix:
\verbatim
.1 => LITERAL_DOUBLE + 8 byte value
? => COND_IF
.1 => LITERAL_DOUBLE + 8 byte value
: => COND_ELSE
...
=> COND_END
\endcode
\endverbatim
* For other short expressions the factor 21/6 always gives a big enough
* postfix buffer (proven by hand, look at '1+' and '.1+' as well).
* postfix buffer (proven by hand, look at \c 1+ and <tt>.1+</tt> as well).
*/
#define INFIX_TO_POSTFIX_SIZE(n) ((n)*21/6)
@@ -115,205 +116,221 @@ extern "C" {
/** \brief Compile an infix expression into postfix byte-code
*
* Converts an expression from an infix string to postfix byte-code
* Converts an expression from an infix string to postfix byte-code.
*
* \param pinfix Pointer to the infix string
* \param ppostfix Pointer to the postfix buffer
* \param perror Place to return an error code
* \return Non-zero value in event of error
*
* It is the caller's responsibility to ensure that \c ppostfix points
* to sufficient storage to hold the postfix expression. The macro
* INFIX_TO_POSTFIX_SIZE(n) can be used to calculate an appropriate
* postfix buffer size from the length of the infix buffer.
* It is the caller's responsibility to ensure that \p ppostfix points to
* sufficient storage to hold the postfix expression.
* The macro INFIX_TO_POSTFIX_SIZE(n) can be used to calculate an
* appropriate postfix buffer size from the length of the infix buffer.
* The macro's parameter \p n must count the terminating nil byte too.
*
* \note "n" must count the terminating nil byte too.
* -# The **infix expressions** that can be used are very similar to the
* C expression syntax, but with some additions and subtle differences in
* operator meaning and precedence.
* The expression string may contain a series of expressions separated by
* a semi-colon character <tt>;</tt> any one of which may actually provide
* the calculation result.
* However all of the other expressions included must assign their result
* to a variable.
* All alphabetic elements described below are case independent, so upper
* and lower case letters may be used and mixed in the variable and
* function names as desired.
* Spaces may be used anywhere within an expression except between the
* characters that make up a single expression element.
* -# The simplest expression element is a **numeric literal,** any
* (positive) number expressed using the standard floating point syntax
* that can be stored as a double precision value.
* This now includes the values Infinity and NaN (not a number).
* Note that negative numbers will be encoded as a positive literal, to
* which the unary negate operator is applied.
*
* -# The **infix expressions** that can be used are very similar
* to the C expression syntax, but with some additions and subtle
* differences in operator meaning and precedence. The string may
* contain a series of expressions separated by a semi-colon character ';'
* any one of which may actually provide the calculation result; however
* all of the other expressions included must assign their result to
* a variable. All alphabetic elements described below are case independent,
* so upper and lower case letters may be used and mixed in the variable
* and function names as desired. Spaces may be used anywhere within an
* expression except between the characters that make up a single expression element.
* Examples:
* - \c 1
* - \c 2.718281828459
* - \c Inf
*
* -# ***Numeric Literals***
* The simplest expression element is a numeric literal, any (positive)
* number expressed using the standard floating point syntax that can be stored
* as a double precision value. This now includes the values Infinity and
* NaN (not a number). Note that negative numbers will be encoded as a
* positive literal to which the unary negate operator is applied.
*
* - Examples:
* - 1
* - 2.718281828459
* - Inf
*
* -# ***Constants***
* There are three trigonometric constants available to any expression
* -# There are three **trigonometric constants** available to any expression
* which return a value:
* - pi returns the value of the mathematical constant pi.
* - D2R evaluates to pi/180 which, when used as a multiplier,
* converts an angle from degrees to radians.
* - R2D evaluates to 180/pi which as a multiplier converts an angle
* from radians to degrees.
* - \c pi returns the value of the mathematical constant pi.
* - \c D2R evaluates to pi/180 which, when used as a multiplier,
* converts an angle from degrees to radians.
* - \c R2D evaluates to 180/pi which as a multiplier converts an
* angle from radians to degrees.
*
* -# ***Variables***
* Variables are used to provide inputs to an expression, and are named
* using the single letters A through U inclusive or the keyword VAL which
* refers to the previous result of this calculation. The software that
* makes use of the expression evaluation code should document how the
* individual variables are given values; for the calc record type the input
* links INPA through INPU can be used to obtain these from other record fields,
* and VAL refers to the the VAL field (which can be overwritten from outside
* the record via Channel Access or a database link).
* -# **Variables** are used to provide inputs to an expression, and are
* named using the single letters \c A through \c U inclusive or the
* keyword \c VAL which refers to the previous result of this
* calculation.
* The software that makes use of the expression evaluation code should
* document how the individual variables are given values.
* For the calc and calcout record types the input links \c INPA through
* \c INPU can be used to obtain values from other record fields, and \c
* VAL refers to the the VAL field (which can be overwritten from
* outside the record via Channel Access or a database link).
*
* -# ***Variable Assignment Operator***
* Recently added is the ability to assign the result of a sub-expression to
* any of the single letter variables, which can then be used in another
* sub-expression. The variable assignment operator is the character pair
* := and must immediately follow the name of the variable to receive the
* expression value. Since the infix string must return exactly one value, every
* -# The **Variable Assignment Operator** was added in 3.14.9 and
* provides the ability to assign the result of a sub-expression to any
* of the single letter variables, which can then be used in later
* sub-expressions.
* The variable assignment operator is the character pair <tt>:=</tt> and
* must immediately follow the name of the variable to receive the
* expression value.
* Since the infix string must return exactly one value, every
* expression string must have exactly one sub-expression that is not an
* assignment, which can appear anywhere in the string. Sub-expressions within
* the string are separated by a semi-colon character.
* assignment, which can appear anywhere in the string.
* Sub-expressions within the string are separated by a semi-colon
* character <tt>;</tt> .
*
* - Examples:
* - B; B:=A
* - i:=i+1; a*sin(i*D2R)
* Examples:
* - <tt>B; B:=A</tt>
* - <tt>i:=i+1; a*sin(i*D2R)</tt>
*
* -# ***Arithmetic Operators***
* The usual binary arithmetic operators are provided: + - * and / with their
* usual relative precedence and left-to-right associativity, and - may also
* be used as a unary negate operator where it has a higher precedence and
* associates from right to left. There is no unary plus operator, so numeric
* literals cannot begin with a + sign.
* -# The standard binary **Arithmetic Operators** are provided: <tt>+ -
* *</tt> and \c / with their usual relative precedence and
* left-to-right associativity.
* A minus sign \c - may also be used as a unary negate operator where
* it has a higher precedence and associates from right to left.
* There is no unary plus operator, so numeric literals cannot begin
* with a plus sign \c + .
*
* - Examples:
* - a*b + c
* - a/-4 - b
* Examples:
* - <tt>a*b + c</tt>
* - <tt>a/-4 - b</tt>
*
* Three other binary operators are also provided: % is the integer modulo operator,
* while the synonymous operators ** and ^ raise their left operand to the power of
* the right operand. % has the same precedence and associativity as * and /, while
* the power operators associate left-to-right and have a precedence in between * and
* unary minus.
* Three other binary operators are also provided:
* \c % is the integer modulo operator, while the synonymous operators
* \c ** and \c ^ raise their left operand to the power of the right
* operand.
* \c % has the same precedence and associativity as \c * and \c /,
* while the power operators associate left-to-right and have a
* precedence in between \c * and unary minus \c - .
*
* - Examples:
* - e:=a%10
* - d:=a/10%10
* - c:=a/100%10
* - b:=a/1000%10
* - b*4096+c*256+d*16+e
* - sqrt(a**2 + b**2)
* Examples:
* - <tt>e:=a%10</tt>
* - <tt>d:=a/10%10</tt>
* - <tt>c:=a/100%10</tt>
* - <tt>b:=a/1000%10</tt>
* - <tt>b*4096+c*256+d*16+e</tt>
* - <tt>sqrt(a**2 + b**2)</tt>
*
* -# ***Algebraic Functions***
* Various algebraic functions are available which take parameters inside
* parentheses. The parameter separator is a comma.
* -# Various **Algebraic Functions** are available which take parameters
* inside parentheses.
* The parameter separator is a comma <tt>,</tt> .
*
* - Absolute value: abs(a)
* - Exponential ea: exp(a)
* - Logarithm, base 10: log(a)
* - Natural logarithm (base e): ln(a) or loge(a)
* - n parameter maximum value: max(a, b, ...)
* - n parameter minimum value: min(a, b, ...)
* - Square root: sqr(a) or sqrt(a)
* - Floating point modulo: fmod(num, den)
* \since The fmod() function was added in 7.0.8
* - Absolute value: \c abs(a)
* - Exponential ea: \c exp(a)
* - Logarithm, base 10: \c log(a)
* - Natural logarithm (base e): \c ln(a) or \c loge(a)
* - n parameter maximum value: <tt>max(a, b, ...)</tt>
* - n parameter minimum value: <tt>min(a, b, ...)</tt>
* - Square root: \c sqr(a) or \c sqrt(a)
* - Floating point modulo: <tt>fmod(num, den)</tt>
* <br>The \c fmod() function was added in 7.0.8
*
* -# ***Trigonometric Functions***
* Standard circular trigonometric functions, with angles expressed in radians:
* - Sine: sin(a)
* - Cosine: cos(a)
* - Tangent: tan(a)
* - Arcsine: asin(a)
* - Arccosine: acos(a)
* - Arctangent: atan(a)
* - 2 parameter arctangent: atan2(a, b)
* \note Note that these arguments are the reverse of the ANSI C function,
* so while C would return arctan(a/b) the calc expression engine returns arctan(b/a)
* -# Standard circular **Trigonometric Functions** exist with angles
* expressed in radians:
*
* -# ***Hyperbolic Trigonometry***
* The basic hyperbolic functions are provided, but no inverse functions
* (which are not provided by the ANSI C math library either).
* - Hyperbolic sine: sinh(a)
* - Hyperbolic cosine: cosh(a)
* - Hyperbolic tangent: tanh(a)
* - Sine: \c sin(a)
* - Cosine: \c cos(a)
* - Tangent: \c tan(a)
* - Arcsine: \c asin(a)
* - Arccosine: \c acos(a)
* - Arctangent: \c atan(a)
* - 2 parameter arctangent: <tt>atan2(a, b)</tt>
* <br>Note that the \c atan2 arguments are the reverse of the ANSI C
* function, so while C would return \c arctan(a/b) the calc
* expression engine returns \c arctan(b/a)
*
* -# ***Numeric Functions***
* The numeric functions perform operations related to the floating point
* numeric representation and truncation or rounding.
* - Round up to next integer: ceil(a)
* - Round down to next integer: floor(a)
* - Round to nearest integer: nint(a)
* - Test for infinite result: isinf(a)
* - Test for any non-numeric values: isnan(a, ...)
* - Test for all finite, numeric values: finite(a, ...)
* - Random number between 0 and 1: rndm
* -# The basic **Hyperbolic Trigonometry** functions are provided, but
* no inverse functions (which aren't provided by the ANSI C math
* library either).
*
* -# ***Boolean Operators***
* These operators regard their arguments as true or false, where 0.0 is
* false and any other value is true.
* - Hyperbolic sine: \c sinh(a)
* - Hyperbolic cosine: \c cosh(a)
* - Hyperbolic tangent: \c tanh(a)
*
* - Boolean and: a && b
* - Boolean or: a || b
* - Boolean not: !a
* -# These **Numeric Functions** perform operations related to the
* floating point numeric representation and truncation or rounding.
*
* -# ***Bitwise Operators***
* Most bitwise operators convert their arguments to 32-bit signed integer (by
* truncation), perform the appropriate bitwise operation, then convert back
* to a floating point value. The arithmetic right shift operator >> thus
* retains the sign bit of the left-hand argument. The logical right shift
* operator >>> is performed on an unsigned integer though, so injects zeros
* while shifting. The right-hand shift argument is masked so only the lower
* 5 bits are used. Unlike in C, ^ is not a bitwise exclusive-or operator.
* - Round up to next integer: \c ceil(a)
* - Round down to next integer: \c floor(a)
* - Round to nearest integer: \c nint(a)
* - Test for infinite result: \c isinf(a)
* - Test for any non-numeric values: <tt>isnan(a, ...)</tt>
* - Test for all finite, numeric values: <tt>finite(a, ...)</tt>
* - Random number between 0 and 1: \c rndm
*
* - Bitwise and: a & b or a and b
* - Bitwise or: a | b or a or b
* - Bitwise exclusive or: a xor b
* - Bitwise not (ones complement): ~a or not a
* - Arithmetic left shift: a << b
* - Arithmetic right shift: a >> b
* - Logical right shift: a >>> b
* -# The **Boolean Operators** evaluate their arguments as true or
* false, where \c 0.0 is false and any other value is true.
*
* -# ***Relational Operators***
* Standard numeric comparisons between two values:
* - Boolean and: <tt>a && b</tt>
* - Boolean or: <tt>a || b</tt>
* - Boolean not: \c !a
*
* - Less than: a < b
* - Less than or equal to: a <= b
* - Equal to: a = b or a == b
* - Greater than or equal to: a >= b
* - Greater than: a > b
* - Not equal to: a != b or a # b
* -# Most **Bitwise Operators** convert their arguments to 32-bit signed
* integer (by truncation), perform the appropriate bitwise operation,
* then convert back to a floating point value.
* The arithmetic right shift operator \c >> thus retains the sign bit of
* the left-hand argument.
* The logical right shift operator \c >>> is performed on an unsigned
* integer though, so it injects zeros while shifting.
* The right-hand shift argument is masked so only the lower 5 bits are
* used.
* Unlike in C, \c ^ is not a bitwise exclusive-or operator.
*
* -# ***Conditional Operator***
* Expressions can use the C conditional operator, which has a lower
* precedence than all of the other operators except for the assignment operator.
* - Bitwise and: <tt>a & b</tt> or <tt>a and b</tt>
* - Bitwise or: <tt>a | b</tt> or <tt>a or b</tt>
* - Bitwise exclusive or: <tt>a xor b</tt>
* - Bitwise not (ones complement): <tt>~a</tt> or <tt>not a</tt>
* - Arithmetic left shift: <tt>a << b</tt>
* - Arithmetic right shift: <tt>a >> b</tt>
* - Logical right shift: <tt>a >>> b</tt>
*
* - condition ? true result : false result
* - Example:
* - a < 360 ? a+1 : 0
* -# The **Relational Operators** perform numeric comparisons between
* two double-precision values:
*
* -# ***Parentheses***
* Sub-expressions can be placed within parentheses to override operator presence rules.
* Parentheses can be nested to any depth, but the intermediate value stack used by
* the expression evaluation engine is limited to 80 results (which require an
* expression at least 321 characters long to reach).
* - Less than: <tt>a < b</tt>
* - Less than or equal to: <tt>a <= b</tt>
* - Equal to: <tt>a = b</tt> or <tt>a == b</tt>
* - Greater than or equal to: <tt>a >= b</tt>
* - Greater than: <tt>a > b</tt>
* - Not equal to: <tt>a != b</tt> or <tt>a # b</tt>
*
* -# Expressions can use the C **Conditional Operator**, which has a
* lower precedence than all of the other operators except for the
* assignment operator.
*
* - \a condition <tt>?</tt> \a true-expression <tt>:</tt>
* \a false-expression
* - Example:
* <tt>a < 360 ? a+1 : 0</tt>
*
* -# Sub-expressions can be placed within **Parentheses** <tt>()</tt>
* to override operator presence rules.
* Parentheses can be nested to any depth, but the intermediate value
* stack used by the expression evaluation engine is limited to 80
* results (which takes an expression at least 321 characters long to
* reach).
*/
LIBCOM_API long
postfix(const char *pinfix, char *ppostfix, short *perror);
/** \brief Run the calculation engine
*
* Evaluates the postfix expression against a set ot input values.
* Evaluates the postfix expression against a set of input values.
*
* \param parg Pointer to an array of double values for the arguments A-U
* that can appear in the expression. Note that the argument values may be
* modified if the expression uses the assignment operator.
* \param presult Where to put the calculated result, which may be a NaN or Infinity.
* \param parg Pointer to an array of double values for the arguments
* \c A-U that can appear in the expression.
* Note that the argument values may be modified if the expression uses
* the assignment operator.
* \param presult Where to put the calculated result, which may be a NaN
* or Infinity.
* \param ppostfix The postfix expression created by postfix().
* \return Status value 0 for OK, or non-zero if an error is discovered
* during the evaluation process.
@@ -423,7 +423,7 @@ void resTable<T,ID>::show ( unsigned level ) const
mean, stdDev, maxEntries );
printf("%u empty buckets\n", empty);
if ( X != this->nInUse ) {
printf ("this->nInUse didnt match items counted which was %f????\n", X );
printf ("this->nInUse didn't match items counted which was %f????\n", X );
}
}
}
@@ -1106,7 +1106,7 @@ stringId::stringId (const char * idIn, allocationType typeIn) :
if (typeIn==copyString) {
unsigned nChars = strlen (idIn) + 1u;
this->pStr = new char [nChars];
memcpy ( (void *) this->pStr, idIn, nChars );
memcpy ( const_cast<char*>(this->pStr), idIn, nChars );
}
else {
this->pStr = idIn;
@@ -1140,7 +1140,7 @@ stringId::~stringId()
//
// the HP-UX compiler gives us a warning on
// each cast away of const, but in this case
// it cant be avoided.
// it can't be avoided.
//
// The DEC compiler complains that const isn't
// really significant in a cast if it is present.
+7 -7
View File
@@ -78,7 +78,7 @@ int dbmfInit(size_t size, int chunkItems)
pdbmfPvt = &dbmfPvt;
ellInit(&pdbmfPvt->chunkList);
pdbmfPvt->lock = epicsMutexMustCreate();
/*allign to at least a double*/
/*align to at least a double*/
pdbmfPvt->size = size + size%sizeof(double);
/* layout is
* | itemHeader | REDZONE | size | REDZONE |
@@ -126,7 +126,7 @@ void* dbmfMalloc(size_t size)
pitemHeader = (itemHeader *)pmem;
pitemHeader->pchunkNode = pchunkNode;
pnextFree = &pitemHeader->pnextFree;
*pnextFree = *pfreeList; *pfreeList = (void *)pmem;
*pnextFree = *pfreeList; *pfreeList = pmem;
pdbmfPvt->nFree++;
pmem += pdbmfPvt->allocSize;
}
@@ -154,7 +154,7 @@ void* dbmfMalloc(size_t size)
epicsMutexUnlock(pdbmfPvt->lock);
pmem += sizeof(itemHeader) + REDZONE;
VALGRIND_MEMPOOL_ALLOC(pdbmfPvt, pmem, size);
return((void *)pmem);
return pmem;
}
char * dbmfStrdup(const char *str)
@@ -190,7 +190,7 @@ void dbmfFree(void* mem)
pitemHeader = (itemHeader *)pmem;
if(!pitemHeader->pchunkNode) {
if(dbmfDebug) printf("dbmfGree: mem %p\n",pmem);
free((void *)pmem); pdbmfPvt->nAlloc--;
free(pmem); pdbmfPvt->nAlloc--;
}else {
void **pfreeList = &pdbmfPvt->freeList;
void **pnextFree = &pitemHeader->pnextFree;
@@ -221,7 +221,7 @@ int dbmfShow(int level)
pchunkNode = (chunkNode *)ellFirst(&pdbmfPvt->chunkList);
while(pchunkNode) {
printf("pchunkNode %p nNotFree %d\n",
(void*)pchunkNode,pchunkNode->nNotFree);
pchunkNode,pchunkNode->nNotFree);
pchunkNode = (chunkNode *)ellNext(&pchunkNode->node);
}
}
@@ -229,10 +229,10 @@ int dbmfShow(int level)
void **pnextFree;;
epicsMutexMustLock(pdbmfPvt->lock);
pnextFree = (void**)pdbmfPvt->freeList;
pnextFree = pdbmfPvt->freeList;
while(pnextFree) {
printf("%p\n",*pnextFree);
pnextFree = (void**)*pnextFree;
pnextFree = *pnextFree;
}
epicsMutexUnlock(pdbmfPvt->lock);
}
+4
View File
@@ -146,6 +146,8 @@ LIBCOM_API ELLNODE * ellGet (ELLLIST *pList);
* \brief Deletes and returns the last node from a list.
* \param pList Pointer to list from which to get node
* \return Pointer to the last node from the list, or NULL if the list is empty
*
* \since 3.15.0.1
*/
LIBCOM_API ELLNODE * ellPop (ELLLIST *pList);
/**
@@ -192,6 +194,8 @@ typedef int (*pListCmp)(const ELLNODE* A, const ELLNODE* B);
*
* \note Use of mergesort algorithm based on analysis by
* http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
*
* \since 3.15.5
*/
LIBCOM_API void ellSortStable(ELLLIST *pList, pListCmp pListCmp);
/**
+11 -1
View File
@@ -78,6 +78,13 @@ LIBCOM_API extern const ENV_PARAM IOCSH_HISTSIZE;
LIBCOM_API extern const ENV_PARAM IOCSH_HISTEDIT_DISABLE;
LIBCOM_API extern const ENV_PARAM EPICS_MUTEX_USE_PRIORITY_INHERITANCE;
LIBCOM_API extern const ENV_PARAM EPICS_ABORT_ON_ASSERT;
LIBCOM_API extern const ENV_PARAM EPICS_ALLOW_POSIX_THREAD_PRIORITY_SCHEDULING;
/** @brief List of all parameters.
*
* A NULL terminated array of all ENV_PARAM known to EPICS Base.
* This array is assembled during the EPICS Base build, and
* contains at least the preceding parameters.
*/
LIBCOM_API extern const ENV_PARAM *env_param_list[];
struct in_addr;
@@ -92,10 +99,13 @@ struct in_addr;
* is set to '\0' and NULL is returned.
*
* \param pParam Pointer to config param structure.
* \param bufDim Dimension of parameter buffer
* \param bufDim Dimension of parameter buffer.
* Must be greater than zero.
* \param pBuf Pointer to parameter buffer
* \return Pointer to the environment variable value string, or
* NULL if no parameter value and default value was empty.
*
* \post A terminating nil will be written to pBuf.
*/
LIBCOM_API char * epicsStdCall
envGetConfigParam(const ENV_PARAM *pParam, int bufDim, char *pBuf);
+2 -2
View File
@@ -45,7 +45,7 @@ extern "C" {
*
* Handles EPICS message codes, and "errno" codes.
*
* Copies in a mesage for any status code. Unknown status codes
* Copies in a message for any status code. Unknown status codes
* are printed numerically.
*/
LIBCOM_API void errSymLookup(long status, char *pBuf, size_t bufLength);
@@ -65,7 +65,7 @@ LIBCOM_API void errSymTest(epicsUInt16 modnum, epicsUInt16 begErrNum,
LIBCOM_API void errSymTestPrint(long errNum);
LIBCOM_API int errSymBld(void);
/** @brief Define new custom error code and associate message string.
* @param errNum New error code. Caller is reponsible for avoiding reuse of existing codes.
* @param errNum New error code. Caller is responsible for avoiding reuse of existing codes.
* @param message New message. Pointer stored. Caller must not free pointed storage.
* @return 0 on success
*/
+21 -20
View File
@@ -19,6 +19,8 @@
// 1) This library is not thread safe
//
#include <iostream>
#define instantiateRecourceLib
#include "epicsAssert.h"
#include "epicsThread.h"
@@ -187,19 +189,12 @@ LIBCOM_API void fdManager::process(double delay)
++ioPending;
#ifdef FDMGR_USE_POLL
#if __cplusplus >= 201100L
priv->pollfds.emplace_back(pollfd{
.fd = iter->getFD(),
.events = WIN_POLLEVENT_FILTER(PollEvents[iter->getType()])
});
#else
struct pollfd pollfd;
pollfd.fd = iter->getFD();
pollfd.events = WIN_POLLEVENT_FILTER(PollEvents[iter->getType()]);
pollfd.revents = 0;
priv->pollfds.push_back(pollfd);
#endif
#endif
#ifdef FDMGR_USE_SELECT
FD_SET(iter->getFD(), &priv->fdSets[iter->getType()]);
@@ -372,11 +367,15 @@ fdReg::~fdReg()
//
void fdReg::show(unsigned level) const
{
printf("fdReg at %p\n", this);
if (level > 1u) {
printf("\tstate = %d, onceOnly = %d\n",
state, onceOnly);
}
std::cout << "fdReg at " << this << "\n";
if (level > 1)
std::cout << "\tstate = " << (
state == active ? "active" :
state == pending ? "pending" :
state == limbo ? "limbo" :
"invalid")
<< ", onceOnly = " << (onceOnly ? "true" : "false")
<< "\n";
fdRegId::show(level);
}
@@ -385,15 +384,17 @@ void fdReg::show(unsigned level) const
//
void fdRegId::show(unsigned level) const
{
printf("fdRegId at %p\n", this);
if (level > 1u) {
printf("\tfd = %"
#if defined(_WIN32)
"I"
#endif
"d, type = %d\n",
fd, type);
std::cout << "fdRegId at " << this << "\n";
if (level > 1) {
std::cout << "\tfd = " << fd
<< ", type = " << (
type == fdrRead ? "fdrRead" :
type == fdrWrite ? "fdrWrite" :
type == fdrException ? "fdrException" :
"invalid")
<< "\n";
}
std::cout << std::flush;
}
//
+1 -1
View File
@@ -71,7 +71,7 @@ LIBCOM_API fdctx * epicsStdCall fdmgr_init(void);
* Specify a function to be called with a specified parameter
* after a specified delay relative to the current time
*
* Returns fdmgrNoAlarm (zero) if alarm cant be created
* Returns fdmgrNoAlarm (zero) if alarm can't be created
*/
#define fdmgrNoAlarm 0
LIBCOM_API fdmgrAlarmId epicsStdCall fdmgr_add_timeout(
+3 -3
View File
@@ -41,7 +41,7 @@ Changes between 2.3 Patch #2 (02Aug90) and original 2.3 release:
Reordered #ifdef maze in the scanner skeleton in the hope of
getting the declarations right for cfront and g++, too.
- Note that this patch supercedes patch #1 for release 2.3,
- Note that this patch supersedes patch #1 for release 2.3,
which was never announced but was available briefly for
anonymous ftp.
@@ -56,7 +56,7 @@ Changes between 2.3 (full) release of 28Jun90 and 2.2 (alpha) release:
given. To specify an end-of-file action for just the initial
state, use <INITIAL><<EOF>>.
- -d debug output is now contigent on the global yy_flex_debug
- -d debug output is now contingent on the global yy_flex_debug
being set to a non-zero value, which it is by default.
- A new macro, YY_USER_INIT, is provided for the user to specify
@@ -99,7 +99,7 @@ Changes between 2.3 (full) release of 28Jun90 and 2.2 (alpha) release:
- yy_switch_to_buffer() can be used in the yywrap() macro/routine.
- flex scanners do not use stdio for their input, and hence when
writing an interactive scanner one must explictly call fflush()
writing an interactive scanner one must explicitly call fflush()
after writing out a prompt.
- flex scanner can be made reentrant (after a fashion) by using
+1 -1
View File
@@ -341,7 +341,7 @@
- yy_create_buffer( file, size ) takes a <I>FILE</I> pointer and
an integer <I>size</I>. It returns a YY_BUFFER_STATE handle to
a new input buffer large enough to accomodate <I>size</I>
a new input buffer large enough to accommodate <I>size</I>
characters and associated with the given file. When in
doubt, use YY_BUF_SIZE for the size.
+7 -7
View File
@@ -574,7 +574,7 @@ extern void *reallocate_array(void *array, int size, int element_size);
(int *) allocate_array( size, sizeof( int ) )
#define reallocate_integer_array(array,size) \
(int *) reallocate_array( (void *) array, size, sizeof( int ) )
(int *) reallocate_array( array, size, sizeof( int ) )
#define allocate_int_ptr_array(size) \
(int **) allocate_array( size, sizeof( int * ) )
@@ -587,22 +587,22 @@ extern void *reallocate_array(void *array, int size, int element_size);
allocate_array( size, sizeof( union dfaacc_union ) )
#define reallocate_int_ptr_array(array,size) \
(int **) reallocate_array( (void *) array, size, sizeof( int * ) )
(int **) reallocate_array( array, size, sizeof( int * ) )
#define reallocate_char_ptr_array(array,size) \
(char **) reallocate_array( (void *) array, size, sizeof( char * ) )
(char **) reallocate_array( array, size, sizeof( char * ) )
#define reallocate_dfaacc_union(array, size) \
(union dfaacc_union *) \
reallocate_array( (void *) array, size, sizeof( union dfaacc_union ) )
reallocate_array( array, size, sizeof( union dfaacc_union ) )
#define allocate_character_array(size) \
(Char *) allocate_array( size, sizeof( Char ) )
#define reallocate_character_array(array,size) \
(Char *) reallocate_array( (void *) array, size, sizeof( Char ) )
(Char *) reallocate_array( array, size, sizeof( Char ) )
#if 0 /* JRW this might couse trouble... but not for IOC usage */
#if 0 /* JRW this might cause trouble... but not for IOC usage */
/* used to communicate between scanner and parser. The type should really
* be YYSTYPE, but we can't easily get our hands on it.
*/
@@ -710,7 +710,7 @@ extern void lerrsf (char[], char[]) NORETURN;
/* spit out a "# line" statement */
extern void line_directive_out (FILE*);
/* generate a data statment for a two-dimensional array */
/* generate a data statement for a two-dimensional array */
extern void mk2data (int);
/* generate a data statement */
+2 -2
View File
@@ -76,7 +76,7 @@ void *allocate_array(int size, int element_size)
if ( element_size * size <= 0 )
flexfatal( "request for < 1 byte in allocate_array()" );
mem = (void *) malloc( (unsigned) (element_size * size) );
mem = malloc( (size_t) element_size * size );
if ( mem == NULL )
flexfatal( "memory allocation failed in allocate_array()" );
@@ -705,7 +705,7 @@ void *reallocate_array(void *array, int size, int element_size)
flexfatal( "attempt to increase array size by less than 1 byte" );
new_array =
(void *) realloc( (char *)array, (unsigned) (size * element_size ));
realloc( array, (size_t) size * element_size );
if ( new_array == NULL )
flexfatal( "attempt to increase array size failed" );
+3 -3
View File
@@ -299,7 +299,7 @@ void expand_nxt_chk(void)
nxt = reallocate_integer_array( nxt, current_max_xpairs );
chk = reallocate_integer_array( chk, current_max_xpairs );
memset( (char *) (chk + old_max), 0,
memset( chk + old_max, 0,
MAX_XPAIRS_INCREMENT * sizeof( int ) / sizeof( char ) );
}
@@ -423,7 +423,7 @@ void inittbl(void)
{
int i;
memset( (char *) chk, 0,
memset( chk, 0,
current_max_xpairs * sizeof( int ) / sizeof( char ) );
tblend = 0;
@@ -500,7 +500,7 @@ void mkdeftbl(void)
* (i.e., jam entries) into the table. It is assumed that by linking to
* "JAMSTATE" they will be taken care of. In any case, entries in "state"
* marking transitions to "SAME_TRANS" are treated as though they will be
* taken care of by whereever "deflink" points. "totaltrans" is the total
* taken care of by wherever "deflink" points. "totaltrans" is the total
* number of transitions out of the state. If it is below a certain threshold,
* the tables are searched for an interior spot that will accommodate the
* state array.
+2 -2
View File
@@ -81,7 +81,7 @@ LIBCOM_API void epicsStdCall
pfl->mallochead = NULL;
pfl->nBlocksAvailable = 0u;
pfl->lock = epicsMutexMustCreate();
*ppvt = (void *)pfl;
*ppvt = pfl;
VALGRIND_CREATE_MEMPOOL(pfl, REDZONE, 0);
}
@@ -118,7 +118,7 @@ LIBCOM_API void * epicsStdCall freeListMalloc(void *pvt)
* | RED | size0 ------ | RED | size1 | ... | RED |
* | | next | ----- |
*/
ptemp = (void *)malloc(pfl->nmalloc*(pfl->size+REDZONE)+REDZONE);
ptemp = malloc(pfl->nmalloc*(pfl->size+REDZONE)+REDZONE);
if(ptemp==0) {
epicsMutexUnlock(pfl->lock);
return(0);
+1 -1
View File
@@ -163,7 +163,7 @@ void epicsStdCall gphDelete(gphPvt *pgphPvt, const char *name, void *pvtid)
if (pvtid == pgphNode->pvtid &&
strcmp(name, pgphNode->name) == 0) {
ellDelete(plist, (ELLNODE*)pgphNode);
free((void *)pgphNode);
free(pgphNode);
break;
}
pgphNode = (GPHENTRY *) ellNext((ELLNODE*)pgphNode);
+4 -4
View File
@@ -83,7 +83,7 @@ typedef enum {
initHookAfterInitDatabase, /**< Records and locksets init (also autosave pass 1) */
initHookAfterFinishDevSup, /**< Device support init pass 1 */
initHookAfterScanInit, /**< Scan, AS, ProcessNotify init */
initHookAfterInitialProcess, /**< Records with PINI = YES processsed */
initHookAfterInitialProcess, /**< Records with PINI = YES processed */
initHookAfterCaServerInit, /**< RSRV init */
initHookAfterIocBuilt, /**< End of iocBuild() */
@@ -154,7 +154,7 @@ typedef enum {
/** \brief Type for application callback functions
*
* Application callback functions must match this typdef.
* Application callback functions must match this typedef.
* \param state initHook enumeration value
*/
typedef void (*initHookFunction)(initHookState state);
@@ -163,9 +163,9 @@ typedef void (*initHookFunction)(initHookState state);
*
* Registers \p func for initHook notifications
* \param func Pointer to application's notification function.
* \return Always zero. (before UNRELEASED could return -1 on allocation failure)
* \return Always zero. (before 7.0.10 could return -1 on allocation failure)
*
* \since UNRELEASED initHookRegister is idempotent.
* \since 7.0.10 initHookRegister is idempotent.
* Previously, repeated registrations would result
* in duplicate calls to the hook function.
*/
+29 -24
View File
@@ -26,22 +26,12 @@
#define EPICS_PRIVATE_API
#include "epicsMath.h"
#include "errlog.h"
#include "macLib.h"
#include "epicsStdio.h"
#include "epicsString.h"
#include "epicsStdlib.h"
#include "epicsThread.h"
#include "epicsMutex.h"
#include "envDefs.h"
#include "registry.h"
// Recent readline.h uses printf in an attribute
#define epicsStdioStdStreams
#define epicsStdioStdPrintfEtc
#include "epicsReadline.h"
#include "cantProceed.h"
#include "iocsh.h"
#include "epicsReadlinePvt.h"
#if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE
# include <readline/readline.h>
# include <readline/history.h>
@@ -62,6 +52,19 @@ static const char *rl_basic_quote_characters;
# endif
#endif
#include "epicsMath.h"
#include "errlog.h"
#include "macLib.h"
#include "epicsStdio.h"
#include "epicsString.h"
#include "epicsStdlib.h"
#include "epicsThread.h"
#include "epicsMutex.h"
#include "envDefs.h"
#include "registry.h"
#include "cantProceed.h"
#include "iocsh.h"
extern "C" {
/*
@@ -165,7 +168,7 @@ void iocshRegisterImpl (const iocshFuncDef *piocshFuncDef,
}
n = (struct iocshCommand *) callocMustSucceed (1, sizeof *n,
"iocshRegister");
if (!registryAdd(iocshCmdID, piocshFuncDef->name, (void *)n)) {
if (!registryAdd(iocshCmdID, piocshFuncDef->name, n)) {
free (n);
errlogPrintf ("iocshRegister failed to add %s\n", piocshFuncDef->name);
return;
@@ -643,8 +646,9 @@ struct ReadlineContext {
if(!hist_file.empty()) {
if(int err = read_history(hist_file.c_str())) {
if(err!=ENOENT)
fprintf(stderr, ERL_ERROR " %s (%d) loading '%s'\n",
strerror(err), err, hist_file.c_str());
fprintf(epicsGetStderr(),
ERL_ERROR " %s (%d) loading '%s'\n",
strerror(err), err, hist_file.c_str());
}
stifle_history(1024); // some limit...
}
@@ -658,8 +662,9 @@ struct ReadlineContext {
#ifdef USE_READLINE
if(!hist_file.empty()) {
if(int err = write_history(hist_file.c_str())) {
fprintf(stderr, ERL_ERROR " %s (%d) writing '%s'\n",
strerror(err), err, hist_file.c_str());
fprintf(epicsGetStderr(),
ERL_ERROR " %s (%d) writing '%s'\n",
strerror(err), err, hist_file.c_str());
}
}
rl_readline_name = prev_rl_readline_name;
@@ -741,7 +746,7 @@ void epicsStdCall iocshRegisterVariable (const iocshVarDef *piocshVarDef)
if (!found) {
n = (struct iocshVariable *) callocMustSucceed(1, sizeof *n,
"iocshRegisterVariable");
if (!registryAdd(iocshVarID, piocshVarDef->name, (void *)n)) {
if (!registryAdd(iocshVarID, piocshVarDef->name, n)) {
free(n);
iocshTableUnlock();
errlogPrintf("iocshRegisterVariable failed to add %s.\n",
@@ -1101,7 +1106,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
return -1;
}
epicsThreadPrivateSet(iocshContextId, (void *) context);
epicsThreadPrivateSet(iocshContextId, context);
}
MAC_HANDLE *handle = context->handle;
@@ -1171,7 +1176,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
if (c == '#') {
if ((prompt == NULL) && (commandLine == NULL))
if (raw[icin + 1] != '-') {
printf(ANSI_BLUE("%s") "\n", raw);
fprintf(epicsGetStdout(), ANSI_BLUE("%s") "\n", raw);
}
continue;
}
@@ -1198,7 +1203,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
*/
if ((prompt == NULL) && *line && (commandLine == NULL)) {
if ((c != '#') || (line[icin + 1] != '-')) {
printf(ANSI_BOLD("%s") "\n", line);
fprintf(epicsGetStdout(), ANSI_BOLD("%s") "\n", line);
}
}
@@ -1512,7 +1517,7 @@ static const iocshArg *onArgs[1] = {&onArg0};
static const iocshFuncDef onFuncDef = {"on", 1, onArgs,
"Change IOC shell error handling.\n"
" continue (default) - Ignores error and continue with next commands.\n"
" break - Return to caller without executing futher commands.\n"
" break - Return to caller without executing further commands.\n"
" halt - Suspend process.\n"
" wait - stall process for <delay> seconds, then continue.\n"};
static void onCallFunc(const iocshArgBuf *args)
+190 -99
View File
@@ -12,23 +12,23 @@
/**
* @file iocsh.h
*
* @brief C and C++ defintions of functions for IOC shell programming.
*
*
* @brief C and C++ definitions of functions for IOC shell programming.
*
* @details
* The iocsh API provides an interface for running commands in the shell
* of the IOC, as well as registering commands and variables for use in the shell.
* It consists of 4 functions for the former and 2 functions for the latter.
*
*
* @par Command functions:
* int iocsh (const char *pathname)@n
* int iocshLoad (const char *pathname, const char *macros)@n
* int iocshCmd (const char *cmd)@n
* int iocshRun (const char *cmd, const char *macros)
*
* - iocsh()
* - iocshLoad()
* - iocshCmd()
* - iocshRun()
*
* @par Registration functions:
* void iocshRegister (const iocshFuncDef * piocshFuncDef, iocshCallFunc func)@n
* void epicsStdCall iocshRegisterVariable (const iocshVarDef *piocshVarDef)
* - iocshRegister()
* - iocshRegisterVariable()
*/
#ifndef INCiocshH
@@ -49,22 +49,47 @@ extern "C" {
#endif
/**
* @enum iocshArgType
*
* This typedef lists the values that can be used as argument data types
* when building the piocshFuncDef parameter of iocshRegister().
*
* @code {.cpp}
* static const iocshArg AsynGenericConfigArg0 = {"Port Name", iocshArgString};
* static const iocshArg AsynGenericConfigArg1 = {"Number Devices", iocshArgInt};
* @endcode
* when building the first parameter of iocshRegister().
*
* ```
* static const iocshArg AsynGenericConfigArg0 = {
* // name
* "Port Name",
* // type
* iocshArgString,
* };
* static const iocshArg AsynGenericConfigArg1 = {
* // name
* "Number Devices",
* // type
* iocshArgInt,
* };
* ```
*/
typedef enum {
/** The argument is converted to an integer value. */
iocshArgInt,
/** The argument is converted to a double-precision floating point value. */
iocshArgDouble,
/** The argument is left as a string.
*
* The memory used to hold the string is "owned" by iocsh
* and will be reused once the handler function returns.
*/
iocshArgString,
/** The argument must be pdbbase. */
iocshArgPdbbase,
/** An arbitrary number of arguments is expected.
*
* Subsequent iocshArg structures will be ignored.
*/
iocshArgArgv,
/** A copy of the argument will be made and a pointer to the copy will be passed to the handler.
*
* The called function should eventually release this copy
* by using the pointer as an argument to free().
*/
iocshArgPersistentString,
/**
* Equivalent to iocshArgString with a hint for tab completion that the
@@ -81,26 +106,46 @@ typedef enum {
}iocshArgType;
/**
* @union iocshArgBuf
*
* This union is used when building the func paramter of iocshRegister().
* Each use should match the parameter type of the parameters of the
* This union is used when building the func parameter of iocshRegister().
* Each use should match the parameter iocshArgType of the parameters of the
* function being registered
*
* @code {.cpp}
*
* ```
* static void AsynGenericConfigCallFunc (const iocshArgBuf *args)
* {
* AsynGenericConfig (args[0].sval, args[1].ival);
* }
* @endcode
* ```
*/
typedef union iocshArgBuf {
/** The value as an integer.
*
* Corresponds to the @ref iocshArgInt type.
*/
int ival;
/** The value as a double-precision floating point.
*
* Corresponds to the @ref iocshArgDouble type.
*/
double dval;
/** The value as a string.
*
* Corresponds to the @ref iocshArgString and related types.
*/
char *sval;
/** The value as an untyped pointer.
*
* Can be used with the @ref iocshArgPdbbase type.
*/
void *vval;
/** The variadic arguments, for the @ref iocshArgArgv type. */
struct {
/** Number of arguments passed to the IOC shell command.
*
* Provides the number of elements of the `av` array.
*/
int ac;
/** The arguments, as an array of strings. */
char **av;
}aval;
}iocshArgBuf;
@@ -119,67 +164,85 @@ typedef struct iocshVarDef {
}iocshVarDef;
/**
* @struct iocshArg
*
* This struct is used to indicate data types of function parameters
* for iocshRegister(). The name element is used by the help command to print
* a synopsis for the command. The type element describes the data type of
* the argument and takes a value from iocshArgType.
*
* @code {.cpp}
* static const iocshArg AsynGenericConfigArg0 = {"Port Name", iocshArgString};
* static const iocshArg AsynGenericConfigArg1 = {"Number Devices", iocshArgInt};
* static const iocshArg* const AsynGenericConfigArgs[]
= { &AsynAXEConfigArg0, &AsynAXEConfigArg1 };
* @endcode
* Data types of function parameters for iocshRegister().
*
* @par Example:
* ```
* static const iocshArg AsynGenericConfigArg0 = {
* // name
* "Port Name",
* // type
* iocshArgString,
* };
* static const iocshArg AsynGenericConfigArg1 = {
* // name
* "Number Devices",
* // type
* iocshArgInt,
* };
* static const iocshArg* const AsynGenericConfigArgs[] = {
* &AsynGenericConfigArg0,
* &AsynGenericConfigArg1,
* };
* ```
*/
typedef struct iocshArg {
/** Used by the `help` command to print a synopsis for the command. */
const char *name;
/** Data type of the argument. */
iocshArgType type;
}iocshArg;
/**
* @struct iocshFuncDef
*
* This struct is used with iocshRegister to define the function that
* is being registered.
*
* name - the name of the command or function@n
* nargs - the number of entries in the array of pointers to argument descriptions@n
* arg - an array of pointers to structs of type iocshArg@n
*
* @code {.cpp}
* static const iocshFuncDef AsynGenericConfigFuncDef
* = { "AsynGenericConfig", 2, AsynGenericConfigArgs };
* @endcode
*
* Used with iocshRegister() to define the function that is being registered.
*
* @par Example:
* ```
* static const iocshFuncDef AsynGenericConfigFuncDef = {
* // name
* "AsynGenericConfig",
* // nargs
* 2,
* // arg
* AsynGenericConfigArgs,
* // usage
* "Helpful message describing the command",
* };
* ```
*/
typedef struct iocshFuncDef {
/** Name of the command or function. */
const char *name;
/** Number of entries in the array of pointers to argument descriptions.
*
* If 0, `arg` can be `NULL`.
*/
int nargs;
/** Array of pointers to structs of type iocshArg.
*
* Can be `NULL` if `nargs` is 0.
*/
const iocshArg * const *arg;
/** Text displayed when using running `help <command>`. */
const char* usage;
}iocshFuncDef;
#define IOCSHFUNCDEF_HAS_USAGE
/**
* @typedef
*
* This typedef defines a function that is used as the *piocshFuncDef
* parameter of iocshRegister().
*
* @code {.cpp}
* This typedef defines a function that is used
* as the first parameter of iocshRegister().
*
* ```
* static void AsynGenericConfigCallFunc (const iocshArgBuf *args)
* {
* AsynGenericConfig (args[0].sval, args[1].ival);
* }
*
*
* static void AsynGenericRegister(void)
* {
* iocshRegister(&AsynGenericConfigFuncDef, AsynGenericConfigCallFunc);
* }
* @endcode
* ```
*/
typedef void (*iocshCallFunc)(const iocshArgBuf *argBuf);
@@ -194,11 +257,12 @@ typedef struct iocshCmdDef {
}iocshCmdDef;
/**
* @brief This function is used to register a command with the IOC shell
*
* @param piocshFuncDef A pointer to a data structure that describes the command and its arguments.
* @param func A pointer to a function which is called by iocsh() when the command is encountered.
* @return void
* @brief Register a command with the IOC shell.
*
* @param[in] piocshFuncDef
* A pointer to a data structure that describes the command and its arguments.
* See the IOC Shell section of the Application Developer's Guide for more information.
* @param[in] func A pointer to a function which is called by iocsh() when the command is encountered.
*/
LIBCOM_API void epicsStdCall iocshRegister(
const iocshFuncDef *piocshFuncDef, iocshCallFunc func);
@@ -243,46 +307,73 @@ LIBCOM_API const iocshVarDef * epicsStdCall iocshFindVariable(
*/
LIBCOM_API void epicsStdCall iocshFree(void);
/**
* @brief This function is used to execute IOC shell commands from a file.
*
* Commands are read from the file until and exit command is encountered or the
* end-of-file character is reached.
*
* @param pathname A string that represents the path to a file from which commands are read.
* @return 0 on success, non-zero on error
*
/**
* @brief Read and evaluate IOC shell commands from the given file.
*
* Equivalent to:
* @code iocshLoad(pathname, NULL) @endcode */
* @code iocshLoad(pathname, NULL) @endcode
*
* @see iocshLoad()
*/
LIBCOM_API int epicsStdCall iocsh(const char *pathname);
/**
* @brief This function is used to exectute a single IOC shell command.
*
* @param cmd A string that represents the command to be executed.
* @return 0 on success, non-zero on error
*
* @brief Run a single IOC shell command.
*
* Equivalent to:
* @code iocshRun(cmd, NULL) @endcode */
* @code iocshRun(cmd, NULL) @endcode
*
* @see iocshRun()
*/
LIBCOM_API int epicsStdCall iocshCmd(const char *cmd);
/**
* @brief Read and evaluate IOC shell commands from the given file. A list of macros
* can be supplied as a parameter. These macros are treated as environment variables during
* exectution of the file's commands.
*
* @param pathname A string that represents the path to a file from which commands are read.
* @param macros NULL or a comma separated list of macro definitions. eg. "VAR1=x,VAR2=y"
* @return 0 on success, non-zero on error
/**
* @brief Read and evaluate IOC shell commands from the given file.
*
* Commands are read from the file
* until an `exit` command is encountered
* or end-of-file is reached.
*
* A list of macros can be supplied as a parameter.
* These macros are treated as environment variables
* during execution of the file's commands.
*
* @sa iocsh()
*
* @param[in] pathname
* A string that represents the path to a file from which commands are read.
* If `NULL`, commands are read from the standard input.
* @param[in] macros
* `NULL` or a comma separated list of macro definitions.
* eg. `"VAR1=x,VAR2=y"`
*
* @retval 0 on success
* @retval non-zero on error
* @retval -1 if the specified file can't be opened
*/
LIBCOM_API int epicsStdCall iocshLoad(const char *pathname, const char* macros);
/**
* @brief Evaluate a single IOC shell command. A list of macros can be supplied
* as a parameter. These macros are treated as environment variables during
* exectution of the command.
*
* @param cmd Command string. eg. "echo \"something or other\""
* @param macros NULL or a comma separated list of macro definitions. eg. "VAR1=x,VAR2=y"
* @return 0 on success, non-zero on error
/**
* @brief Run a single IOC shell command.
*
* A list of macros can be supplied as a parameter.
* These macros are treated as environment variables
* during execution of the command.
*
* This function may be run from any thread,
* but many IOC shell commands may not be thread-safe.
*
* @sa iocshCmd()
*
* @param[in] cmd
* Command string.
* eg. `"echo \"something or other\""`
* @param[in] macros
* `NULL` or a comma separated list of macro definitions.
* eg. `"VAR1=x,VAR2=y"`
*
* @retval 0 on success
* @retval non-zero on error
*/
LIBCOM_API int epicsStdCall iocshRun(const char *cmd, const char* macros);
+1 -1
View File
@@ -215,7 +215,7 @@ static void registryDumpCallFunc(const iocshArgBuf *args)
static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0,0,
"Initialize IOC logging\n"
" * EPICS environment variable 'EPICS_IOC_LOG_INET' has to be defined\n"
" * Logging controled via 'iocLogDisable' variable\n"
" * Logging controlled via 'iocLogDisable' variable\n"
" see 'setIocLogDisable' command\n"};
static void iocLogInitCallFunc(const iocshArgBuf *args)
{
+3 -3
View File
@@ -111,7 +111,7 @@ int main(void)
return IOCLS_ERROR;
}
pserver->pfdctx = (void *) fdmgr_init();
pserver->pfdctx = fdmgr_init();
if (!pserver->pfdctx) {
fprintf(stderr, "iocLogServer: %s\n", strerror(errno));
free(pserver);
@@ -134,7 +134,7 @@ int main(void)
epicsSocketEnableAddressReuseDuringTimeWaitState ( pserver->sock );
/* Zero the sock_addr structure */
memset((void *)&serverAddr, 0, sizeof serverAddr);
memset(&serverAddr, 0, sizeof serverAddr);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(ioc_log_port);
@@ -670,7 +670,7 @@ static void writeMessagesToLog (struct iocLogClient *pclient)
}
else {
if (status != ntci) {
fprintf(stderr, "iocLogServer: didnt calculate number of characters correctly?\n");
fprintf(stderr, "iocLogServer: didn't calculate number of characters correctly?\n");
}
pclient->pserver->filePos += status;
}
+2 -2
View File
@@ -503,7 +503,7 @@ logClientId epicsStdCall logClientCreate (
pClient->shutdown = 0;
pClient->shutdownConfirm = 0;
epicsAtExit (logClientDestroy, (void*) pClient);
epicsAtExit (logClientDestroy, pClient);
pClient->stateChangeNotify = epicsEventCreate (epicsEventEmpty);
if ( ! pClient->stateChangeNotify ) {
@@ -533,7 +533,7 @@ logClientId epicsStdCall logClientCreate (
return NULL;
}
return (void *) pClient;
return pClient;
}
/*
+1 -1
View File
@@ -71,7 +71,7 @@ epicsStdCall macCreateHandle(
/**
* \brief Disable or enable warning messages.
*
* The macExpandString() routine prints warnings when it cant expand a macro.
* The macExpandString() routine prints warnings when it can't expand a macro.
* This routine can be used to silence those warnings. A non zero value will
* suppress the warning messages from subsequent library routines given the
* same \c handle.
+2 -2
View File
@@ -28,7 +28,7 @@ a) long macCreateHandle( MAC_HANDLE **handle, char *pairs[] );
routine.
macSuppressWarning can be called to suppress the marning message
when macExpandString cant expand a macro. A non zero value will
when macExpandString can't expand a macro. A non zero value will
suppress the messages.
@@ -125,7 +125,7 @@ a) macParseDefns( MAC_HANDLE *handle, char *defns, char **pairs[] );
significant within values but ignored elsewhere (i.e. surrounding "="
and "," characters).
Probably noone will ever want to, but the special meanings of "$",
Probably no one will ever want to, but the special meanings of "$",
"{", "}", "(", ")", "=" and "," can all be changed via macPutXxxx()
calls. This routine does not have a handle argument, so they must be
changed globally for it to use the new definitions. Should it have a
+8 -8
View File
@@ -78,7 +78,7 @@ epicsStdCall macParseDefns(
del[0] = FALSE;
quote = 0;
state = preName;
for ( c = (const char *) defns; *c != '\0'; c++ ) {
for ( c = defns; *c != '\0'; c++ ) {
/* handle quotes */
if ( quote )
@@ -184,7 +184,7 @@ epicsStdCall macParseDefns(
*memCpp++ = memCp;
/* copy value regardless of the above */
strncpy( memCp, (const char *) ptr[i], end[i] - ptr[i] );
strncpy( memCp, ptr[i], end[i] - ptr[i] );
memCp += end[i] - ptr[i];
*memCp++ = '\0';
}
@@ -224,9 +224,9 @@ epicsStdCall macParseDefns(
}
/* free workspace */
free( ( void * ) ptr );
free( ( void * ) end );
free( ( char * ) del );
free( ( void * ) ptr ); /* cast away const */
free( ( void * ) end ); /* cast away const */
free( del );
/* debug output */
if ( handle != NULL && handle->debug & 1 )
@@ -238,9 +238,9 @@ epicsStdCall macParseDefns(
/* error exit */
error:
errlogPrintf( "macParseDefns: failed to allocate memory\n" );
if ( ptr != NULL ) free( ( void * ) ptr );
if ( end != NULL ) free( ( void * ) end );
if ( del != NULL ) free( ( char * ) del );
if ( ptr != NULL ) free( ( void * ) ptr ); /* cast away const */
if ( end != NULL ) free( ( void * ) end ); /* cast away const */
if ( del != NULL ) free( del );
*pairs = NULL;
return -1;
}
+5 -5
View File
@@ -20,19 +20,19 @@
size_t adjustToWorstCaseAlignment(size_t size)
{
union aline {
/* largest primative types (so far...) */
union align {
/* largest primitive types (so far...) */
double dval;
size_t uval;
char *ptr;
};
/* assert that alignment size is a power of 2 */
STATIC_ASSERT((sizeof(union aline) & (sizeof(union aline)-1))==0);
STATIC_ASSERT((sizeof(union align) & (sizeof(union align)-1))==0);
/* round up to aligment size */
/* round up to alignment size */
size--;
size |= sizeof(union aline)-1;
size |= sizeof(union align)-1;
size++;
return size;
+3 -3
View File
@@ -27,7 +27,7 @@ LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
errlogPrintf("%s: callocMustSucceed(%lu, %lu) - " ERL_ERROR " calloc failed\n",
msg, (unsigned long)count, (unsigned long)size);
errlogPrintf("Thread %s (%p) suspending.\n",
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
epicsThreadGetNameSelf(), epicsThreadGetIdSelf());
errlogFlush();
epicsThreadSuspendSelf();
}
@@ -43,7 +43,7 @@ LIBCOM_API void * mallocMustSucceed(size_t size, const char *msg)
errlogPrintf("%s: mallocMustSucceed(%lu) - " ERL_ERROR " malloc failed\n",
msg, (unsigned long)size);
errlogPrintf("Thread %s (%p) suspending.\n",
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
epicsThreadGetNameSelf(), epicsThreadGetIdSelf());
errlogFlush();
epicsThreadSuspendSelf();
}
@@ -60,7 +60,7 @@ LIBCOM_API void cantProceed(const char *msg, ...)
va_end(pvar);
errlogPrintf(ANSI_RED("CRITICAL ERROR") " Thread %s (%p) can't proceed, suspending.\n",
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
epicsThreadGetNameSelf(), epicsThreadGetIdSelf());
epicsStackTrace();
+1 -1
View File
@@ -22,7 +22,7 @@ LIBCOM_API float epicsConvertDoubleToFloat(double value);
/* dbConvertBase is used in dbPut and dbGet string to integer conversions.
It defaults to 0 but is set to 10 if the EPICS_DB_CONVERT_DECIMAL_ONLY
environment variable is set.
environment variable is YES (case insensitive).
*/
LIBCOM_API extern int dbConvertBase;
+1 -1
View File
@@ -93,7 +93,7 @@ LIBCOM_API size_t epicsStrnEscapedFromRawSize(const char *buf, size_t len);
*/
LIBCOM_API int epicsStrCaseCmp(const char *s1, const char *s2);
/** \brief Does case-insensitive comparision of two strings
/** \brief Does case-insensitive comparison of two strings
*
* Implements strncmp from the C standard library, except is case insensitive
*/
@@ -155,7 +155,7 @@ void ipAddrToAsciiEngine::cleanup()
ipAddrToAsciiEnginePrivate::pEngine = 0;
}
// for now its probably sufficent to allocate one
// for now its probably sufficient to allocate one
// DNS transaction thread for all codes sharing
// the same process that need DNS services but we
// leave our options open for the future
+1 -1
View File
@@ -7,7 +7,7 @@ is like the vxWorks sysClockRateGet().
The implementation should be:
Provide a default that determines the rate emperically at int time by
Provide a default that determines the rate empirically at int time by
calling epicsThreadSleep for amaller and smaller delays until the actual
delay no longer decreases.
Each os/xxx can override the default if desired.
@@ -45,7 +45,11 @@
/*
* Enable format-string checking if possible
*/
#if __GNUC__ * 100 + __GNUC_MINOR__ >= 404
#define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__gnu_printf__,f,a)))
#else
#define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__printf__,f,a)))
#endif
/*
* Deprecation marker
+3 -3
View File
@@ -357,7 +357,7 @@ static long devInstallAddr (
epicsMutexMustLock(addrListLock);
ellDelete(&addrFree[addrType], &pRange->node);
epicsMutexUnlock(addrListLock);
free ((void *)pRange);
free (pRange);
}
else {
pRange->begin = base + size;
@@ -553,7 +553,7 @@ static long devCombineAdjacentBlocks(
pRange->begin = pBefore->begin;
ellDelete (pRangeList, &pBefore->node);
epicsMutexUnlock(addrListLock);
free ((void *)pBefore);
free (pBefore);
}
}
@@ -563,7 +563,7 @@ static long devCombineAdjacentBlocks(
pRange->end = pAfter->end;
ellDelete (pRangeList, &pAfter->node);
epicsMutexUnlock(addrListLock);
free((void *)pAfter);
free(pAfter);
}
}
+1 -1
View File
@@ -11,7 +11,7 @@
/**
* \file devLibVME.h
* \author Marty Kraimer, Jeff Hill
* \brief API for accessing hardware devices, mosty over VMEbus.
* \brief API for accessing hardware devices, mostly over VMEbus.
*
* API for accessing hardware devices. The original APIs here were for
* written for use with VMEbus but additional routines were added for
+2 -2
View File
@@ -18,12 +18,12 @@
*
* These primitives can be safely used in a multithreaded programs on symmetric multiprocessing (SMP)
* systems. Where possible the primitives are implemented with compiler intrinsic wrappers for architecture
* specific instructions. Otherwise they are implemeted with OS specific functions and otherwise, when lacking
* specific instructions. Otherwise they are implemented with OS specific functions and otherwise, when lacking
* a sufficently capable OS specific interface, then in some rare situations a mutual exclusion primitive is
* used for synchronization.
*
* In operating systems environments which allow C code to run at interrupt level the implementation must
* use interrupt level invokable CPU instruction primitives.
* use interrupt level invocable CPU instruction primitives.
*
* All C++ functions are implemented in the namespace atomics which is nested inside of namespace epics.
*/
+2 -2
View File
@@ -25,11 +25,11 @@
#include "osdWireConfig.h"
#ifndef EPICS_BYTE_ORDER
#error osdWireConfig.h didnt define EPICS_BYTE_ORDER
#error osdWireConfig.h did not define EPICS_BYTE_ORDER
#endif
#ifndef EPICS_FLOAT_WORD_ORDER
#error osdWireConfig.h didnt define EPICS_FLOAT_WORD_ORDER
#error osdWireConfig.h did not define EPICS_FLOAT_WORD_ORDER
#endif
#endif /* INC_epicsEndian_H */
+3 -3
View File
@@ -99,7 +99,7 @@ public:
**/
void wait ();
/**\brief Wait for the event or until the specified timeout.
* \param timeout The timeout delay in seconds. A timeout of zero is
* \param timeout The timeout delay in seconds. A timeout of zero or less is
* equivalent to calling tryWait(); NaN or any value too large to be
* represented to the target OS is equivalent to no timeout.
* \return True if the event was triggered, False if it timed out.
@@ -189,10 +189,10 @@ LIBCOM_API epicsEventStatus epicsEventWait(
*/
LIBCOM_API void epicsEventMustWait(epicsEventId id);
/**\brief Wait an the event or until the specified timeout period is over.
/**\brief Wait for the event or until the specified timeout period is over.
* \note Blocks until full or timeout.
* \param id The event identifier.
* \param timeout The timeout delay in seconds. A timeout of zero is
* \param timeout The timeout delay in seconds. A timeout of zero or less is
* equivalent to calling epicsEventTryWait(); NaN or any value too large
* to be represented to the target OS is equivalent to no timeout.
* \return Status indicator.
+2 -2
View File
@@ -13,7 +13,7 @@
/*
* NOTES:
* 1) LOG_LAST_OWNER feature is normally commented out because
* it slows down the system at run time, anfd because its not
* it slows down the system at run time, and because it's not
* currently safe to convert a thread id to a thread name because
* the thread may have exited making the thread id invalid.
*/
@@ -119,7 +119,7 @@ void epicsStdCall epicsMutexShow(
epicsMutexId pmutexNode, unsigned int level)
{
printf("epicsMutexId %p source %s line %d\n",
(void *)pmutexNode, pmutexNode->pFileName,
pmutexNode, pmutexNode->pFileName,
pmutexNode->lineno);
if ( level > 0 ) {
epicsMutexOsdShow(pmutexNode,level-1);
+13 -30
View File
@@ -102,8 +102,16 @@ extern "C" {
#endif
/**
* \brief epicsSnprintf() is meant to have the same semantics as the C99
* function snprintf()
* \brief Wrapper around OS snprintf()
*
* \param str Output buffer. Must be non-NULL.
* \param size Capacity of output buffer, including space for nil.
* Must be a positive value.
* \param format Format string. Must be non-NULL.
* \returns Non-negative value on success (see below).
*
* \post On success, at least a nil charactor has been written to the output buffer.
* On failure, the output buffer state is not specified.
*
* \details
* This is provided because some architectures do not implement these functions,
@@ -124,41 +132,16 @@ extern "C" {
* On these systems epicsSnprintf() can return an error (a value less than
* zero) when a buffer length of zero is passed in, so callers should not use
* that technique to calculate the length of the buffer required.
*
* \return The number of characters (not counting the terminating zero byte)
* that would be written to `str` if it was large enough to hold them all; the
* output has been truncated if the return value is `size` or more.
*/
LIBCOM_API int epicsStdCall epicsSnprintf(
char *str, size_t size, EPICS_PRINTF_FMT(const char *format), ...
) EPICS_PRINTF_STYLE(3,4);
/**
* \brief epicsVsnprintf() is meant to have the same semantics as the C99
* function vsnprintf()
* \brief vararg version of epicsSnprintf()
*
* \details
* This is provided because some architectures do not implement these functions,
* while others implement them incorrectly.
* Standardized as a C99 function, vsnprintf() acts like vsprintf() except that
* the `size` argument gives the maximum number of characters (including the
* trailing zero byte) that may be placed in `str`.
* Wrapper around OS vsnprintf().
*
* On some operating systems though the implementation of this function does
* not always return the correct value. If the OS implementation does not
* correctly return the number of characters that would have been written when
* the output gets truncated, it is not worth trying to fix this as long as
* they return `size-1` instead; the resulting string must always be correctly
* terminated with a zero byte.
*
* In some scenarios the epicsSnprintf() implementation may not provide the
* correct C99 semantics for the return value when `size` is given as zero.
* On these systems epicsSnprintf() can return an error (a value less than
* zero) when a buffer length of zero is passed in, so callers should not use
* that technique to calculate the length of the buffer required.
*
* \return The number of characters (not counting the terminating zero byte)
* that would be written to `str` if it was large enough to hold them all; the
* output has been truncated if the return value is `size` or more.
* \see Documentation for epicsSnprintf()
*/
LIBCOM_API int epicsStdCall epicsVsnprintf(
char *str, size_t size, const char *format, va_list ap);
+13 -1
View File
@@ -179,6 +179,15 @@ typedef struct epicsThreadOpts {
* \param parm Passed to thread main function.
* \param opts Modifiers for the new thread, or NULL to use target specific defaults.
* \return NULL on error
*
* The newly created thread will start running immediately, and may end immediately too!
* If joinable, the caller is responsible to arrange for a later call to epicsThreadMustJoin().
* The returned epicsThreadId remains valid until joined.
* For a non-joinable (detached) thread, the lifetime of the returned epicsThreadId
* ends when that thread returns, and so it must not be used unless the caller has
* external knowledge that the thread is still running.
*
* \since 7.0.2
*/
LIBCOM_API epicsThreadId epicsThreadCreateOpt (
const char * name,
@@ -197,7 +206,10 @@ LIBCOM_API epicsThreadId epicsStdCall epicsThreadMustCreate (
/* This gets undefined in osdThread.h on VxWorks < 6.9 */
#define EPICS_THREAD_CAN_JOIN
/** Wait for a joinable thread to exit (return from its main function) */
/** Wait for a joinable thread to exit (return from its main function).
* Thread must have been created as joinable.
* \since 7.0.2 Test EPICS_THREAD_CAN_JOIN macro for BSP support
*/
LIBCOM_API void epicsThreadMustJoin(epicsThreadId id);
/** Block the current thread until epicsThreadResume(). */
LIBCOM_API void epicsStdCall epicsThreadSuspendSelf(void);
+2 -2
View File
@@ -120,7 +120,7 @@ LIBCOM_API int epicsTimeGetMonotonic ( epicsTimeStamp * pDest );
/** \name ISR-callable
* These routines may be called from an Interrupt Service Routine, and
* will return a value from the last current time or event time provider
* that sucessfully returned a result from the equivalent non-ISR routine.
* that successfully returned a result from the equivalent non-ISR routine.
* @{
*/
/** \brief Get current time into \p *pDest (ISR-safe) */
@@ -329,7 +329,7 @@ public:
/** \brief The default constructor sets the time to the EPICS epoch. */
#if __cplusplus>=201103L
constexpr epicsTime() :ts{} {}
constexpr epicsTime() :ts{0, 0} {}
#else
epicsTime () {
ts.secPastEpoch = ts.nsec = 0u;
@@ -47,7 +47,7 @@ void epicsThreadShowInfo(epicsThreadId pthreadInfo, unsigned int level)
priority = param.sched_priority;
}
fprintf(epicsGetStdout(),"%16.16s %14p %8lu %3d%8d %8.8s%s\n",
pthreadInfo->name,(void *)
pthreadInfo->name,
pthreadInfo,(unsigned long)pthreadInfo->lwpId,
pthreadInfo->osiPriority,priority,
pthreadInfo->isSuspended ? "SUSPEND" : "OK",
@@ -74,6 +74,22 @@ LIBCOM_API void epicsStdCall epicsMessageQueueDestroy(
free(id);
}
LIBCOM_API int epicsStdCall epicsMessageQueueSend(
epicsMessageQueueId id,
void *message,
unsigned int messageSize)
{
return mq_send(id->id, (const char*)message, messageSize, 0);
}
LIBCOM_API int epicsStdCall epicsMessageQueueReceive(
epicsMessageQueueId id,
void *message,
unsigned int messageSize)
{
return mq_receive(id->id, (char*)message, messageSize, NULL);
}
LIBCOM_API int epicsStdCall epicsMessageQueueTrySend(
epicsMessageQueueId id,
@@ -23,6 +23,4 @@ struct epicsMessageQueueOSD {
mqd_t id;
char name[24];
};
#define epicsMessageQueueSend(q,m,l) (mq_send((q)->id, (const char*)(m), (l), 0))
#define epicsMessageQueueReceive(q,m,s) (mq_receive((q)->id, (char*)(m), (s), NULL))
@@ -36,7 +36,7 @@ void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level)
if(!status) priority = param.sched_priority;
}
fprintf(epicsGetStdout(),"%16.16s %14p %12lu %3d%8d %8.8s\n",
pthreadInfo->name,(void *)
pthreadInfo->name,
pthreadInfo,(unsigned long)pthreadInfo->tid,
pthreadInfo->osiPriority,priority,
pthreadInfo->isSuspended?"SUSPEND":"OK");
@@ -362,7 +362,7 @@ static int rtemsDevInterruptInUseVME (unsigned vectorNumber)
return FALSE;
/*
* its a C routine. Does it match a default handler?
* it's a C routine. Does it match a default handler?
*/
for (i=0; i<NELEMENTS(defaultHandlerAddr); i++) {
if (defaultHandlerAddr[i] == psub) {
@@ -35,7 +35,7 @@ LIBCOM_API FILE * epicsStdCall epicsTempFile ()
* condition where two programs end up receiving the
* same temporary file name.
*
* _O_CREAT create if non-existant
* _O_CREAT create if non-existent
* _O_EXCL file must not exist
* _O_RDWR read and write the file
* _O_TEMPORARY delete file on close
+1 -1
View File
@@ -72,7 +72,7 @@ void epicsMutexOsdShow ( struct epicsMutexParm *mutex, unsigned level )
{
(void)level;
printf ("epicsMutex: win32 critical section at %p\n",
(void * ) & mutex->osd );
& mutex->osd );
}
void epicsMutexOsdShowAll(void) {}
+3 -3
View File
@@ -57,7 +57,7 @@ static void osiLocalAddrOnce ( void *raw )
DWORD numifs;
DWORD cbBytesReturned;
memset ( (void *) &addr, '\0', sizeof ( addr ) );
memset ( &addr, '\0', sizeof ( addr ) );
addr.sa.sa_family = AF_UNSPEC;
/* only valid for winsock 2 and above */
@@ -114,7 +114,7 @@ static void osiLocalAddrOnce ( void *raw )
"osiLocalAddr(): only loopback found\n");
fail:
/* fallback to loopback */
memset ( (void *) &addr, '\0', sizeof ( addr ) );
memset ( &addr, '\0', sizeof ( addr ) );
addr.ia.sin_family = AF_INET;
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
osiLocalAddrResult = addr;
@@ -124,7 +124,7 @@ fail:
LIBCOM_API osiSockAddr epicsStdCall osiLocalAddr (SOCKET socket)
{
epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, (void*)&socket);
epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, &socket);
return osiLocalAddrResult;
}
+7 -7
View File
@@ -41,7 +41,7 @@
* prototypes only appear in the windows SDK 8 and above.
* VS2010 supplies sdk 7, but can be upgraded to later SDK
* To accomodate this we suuply prototypes on, for XP
* To accommodate this we supply prototypes on, for XP
* fall back to Tls*() which will build and run
* correctly for epicsThreads, but means that TLS allocations from
* epicsThreadImplicitCreate() will continue to leak (for non-EPICS threads).
@@ -49,13 +49,13 @@
* Also, WINE circa 5.0.3 provides the FLS storage functions, but doesn't
* actually run the dtor function.
*
* we check for existance of _WIN32_WINNT_WIN8 which will only be defined
* we check for existence of _WIN32_WINNT_WIN8 which will only be defined
* in SDK 8 and above. If Visa is detected and SDK < 8 we will supply
* the missing prototypes
*/
#if _WIN32_WINNT >= 0x0600 /* VISTA */
# ifdef _WIN32_WINNT_WIN8 /* Existance means using SDK 8 or higher */
# ifdef _WIN32_WINNT_WIN8 /* Existence means using SDK 8 or higher */
# include <fibersapi.h>
# else
# include <winnt.h> /* for PFLS_CALLBACK_FUNCTION */
@@ -1042,12 +1042,12 @@ static void epicsThreadShowInfo ( epicsThreadId id, unsigned level )
if ( pParm ) {
unsigned long idForFormat = pParm->id;
fprintf ( epicsGetStdout(), "%-15s %-8p %-8lx %-9u %-9s %-7s", pParm->pName,
(void *) pParm, idForFormat, pParm->epicsPriority,
pParm, idForFormat, pParm->epicsPriority,
epics_GetThreadPriorityAsString ( pParm->handle ),
epicsThreadIsSuspended ( id ) ? "suspend" : "ok" );
if ( level ) {
fprintf (epicsGetStdout(), " %-8p %-8p ",
(void *) pParm->handle, (void *) pParm->parm );
pParm->handle, pParm->parm );
}
if(!epicsAtomicGetIntT(&pParm->isRunning))
fprintf (epicsGetStdout(), " ZOMBIE");
@@ -1187,7 +1187,7 @@ LIBCOM_API void epicsStdCall epicsThreadPrivateDelete ( epicsThreadPrivateId p )
*/
LIBCOM_API void epicsStdCall epicsThreadPrivateSet ( epicsThreadPrivateId pPvt, void *pVal )
{
BOOL stat = TlsSetValue ( pPvt->key, (void *) pVal );
BOOL stat = TlsSetValue ( pPvt->key, pVal );
assert (stat);
}
@@ -1196,7 +1196,7 @@ LIBCOM_API void epicsStdCall epicsThreadPrivateSet ( epicsThreadPrivateId pPvt,
*/
LIBCOM_API void * epicsStdCall epicsThreadPrivateGet ( epicsThreadPrivateId pPvt )
{
return ( void * ) TlsGetValue ( pPvt->key );
return TlsGetValue ( pPvt->key );
}
/*
+1 -1
View File
@@ -86,7 +86,7 @@ private:
bool threadHasExited;
void updatePLL ();
static const int pllDelay; /* integer seconds */
// cant be static because of diff btw __stdcall and __cdecl
// can't be static because of diff btw __stdcall and __cdecl
friend unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn );
};
@@ -16,6 +16,7 @@
#include <readline/readline.h>
#include <readline/history.h>
#include "errlog.h"
#include "epicsExit.h"
#include "envDefs.h"
#include "epicsReadlinePvt.h"
@@ -78,7 +79,7 @@ osdReadline (const char *prompt, struct readlineContext *context)
line = malloc(linesize);
if (line == NULL) {
printf("Out of memory!\n");
fprintf(stderr, ERL_ERROR " osdReadline() Out of memory!\n");
return NULL;
}
if (prompt) {
@@ -98,7 +99,7 @@ osdReadline (const char *prompt, struct readlineContext *context)
linesize = linelen + 50;
cp = (char *)realloc(line, linesize);
if (cp == NULL) {
printf ("Out of memory!\n");
fprintf(stderr, ERL_ERROR " osdReadline() Out of memory!\n");
free(line);
line = NULL;
break;
@@ -17,7 +17,7 @@
/* This file must be usable from both C and C++ */
/* if compilation fails because this wasnt found then you may need to define an OS
/* if compilation fails because this wasn't found then you may need to define an OS
specific osdWireConfig.h */
#include <sys/param.h>
@@ -27,7 +27,7 @@
# elif __BYTE_ORDER == __BIG_ENDIAN
# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG
# else
# error EPICS hasnt been ported to run on the <sys/param.h> specified __BYTE_ORDER
# error EPICS has not been ported to run on the <sys/param.h> specified __BYTE_ORDER
# endif
#else
# ifdef BYTE_ORDER
@@ -36,10 +36,10 @@
# elif BYTE_ORDER == BIG_ENDIAN
# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG
# else
# error EPICS hasnt been ported to run on the <sys/param.h> specified BYTE_ORDER
# error EPICS has not been ported to run on the <sys/param.h> specified BYTE_ORDER
# endif
# else
# error <sys/param.h> doesnt specify __BYTE_ORDER or BYTE_ORDER - is an OS specific osdWireConfig.h needed?
# error <sys/param.h> does not specify __BYTE_ORDER or BYTE_ORDER - is an OS specific osdWireConfig.h needed?
# endif
#endif
@@ -49,7 +49,7 @@
# elif __FLOAT_WORD_ORDER == __BIG_ENDIAN
# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_BIG
# else
# error EPICS hasnt been ported to <sys/param.h> specified __FLOAT_WORD_ORDER
# error EPICS has not been ported to <sys/param.h> specified __FLOAT_WORD_ORDER
# endif
#else
# ifdef FLOAT_WORD_ORDER
@@ -58,7 +58,7 @@
# elif FLOAT_WORD_ORDER == BIG_ENDIAN
# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_BIG
# else
# error EPICS hasnt been ported to <sys/param.h> specified FLOAT_WORD_ORDER
# error EPICS has not been ported to <sys/param.h> specified FLOAT_WORD_ORDER
# endif
# else
/* assume that if neither __FLOAT_WORD_ORDER nor FLOAT_WORD_ORDER are
@@ -56,7 +56,7 @@ inline void WireGet < epicsFloat64 > (
{
// copy through union here
// a) prevents over-aggressive optimization under strict aliasing rules
// b) doesnt preclude extra copy operation being optimized away
// b) doesn't preclude extra copy operation being optimized away
union {
epicsFloat64 _f;
epicsUInt32 _u[2];
@@ -94,7 +94,7 @@ inline void WireSet < epicsFloat64 > (
{
// copy through union here
// a) prevents over-aggressive optimization under strict aliasing rules
// b) doesnt preclude extra copy operation being optimized away
// b) doesn't preclude extra copy operation being optimized away
union {
epicsFloat64 _f;
epicsUInt32 _u[2];
@@ -158,7 +158,7 @@ inline void AlignedWireGet < epicsFloat64 > (
{
// copy through union here
// a) prevents over-aggressive optimization under strict aliasing rules
// b) doesnt preclude extra copy operation being optimized away
// b) doesn't preclude extra copy operation being optimized away
union Swapper {
epicsUInt32 _u[2];
epicsFloat64 _f;
@@ -214,7 +214,7 @@ inline void AlignedWireSet < epicsFloat64 > (
{
// copy through union here
// a) prevents over-aggressive optimization under strict aliasing rules
// b) doesnt preclude extra copy operation being optimized away
// b) doesn't preclude extra copy operation being optimized away
union Swapper {
epicsUInt32 _u[2];
epicsFloat64 _f;
+56 -1
View File
@@ -41,6 +41,13 @@
# define SOCK_CLOEXEC (0)
#endif
#if defined(AI_PASSIVE) && !defined(__rtems__)
# define USE_INFO
#else
# define USE_BY
#endif
#ifdef USE_BY
/*
* Protect some routines which are not thread-safe
*/
@@ -60,7 +67,7 @@ static void unlockInfo (void)
{
epicsMutexUnlock (infoMutex);
}
#endif
static size_t nAttached;
@@ -164,6 +171,7 @@ LIBCOM_API void epicsStdCall epicsSocketDestroy ( SOCKET s )
}
}
#ifdef USE_BY
/*
* ipAddrToHostName
* On many systems, gethostbyaddr must be protected by a
@@ -214,6 +222,53 @@ LIBCOM_API int epicsStdCall hostToIPAddr
unlockInfo ();
return ret;
}
#endif
#ifdef USE_INFO
unsigned epicsStdCall ipAddrToHostName(const struct in_addr *pAddr, char *pBuf, unsigned bufSize)
{
osiSockAddr query;
if(!bufSize)
return 0; // non-sense
memset(&query, 0, sizeof(query));
query.ia.sin_family = AF_INET;
query.ia.sin_addr = *pAddr;
int ret = getnameinfo(&query.sa, sizeof(query), pBuf, bufSize, NULL, 0, NI_NAMEREQD);
if(ret==0) {
ret = strlen (pBuf);
} else { // lookup fails
ret = 0; // indicate failure to caller
}
return ret;
}
int epicsStdCall hostToIPAddr(const char *pHostName, struct in_addr *pIPA)
{
struct addrinfo hint, *result = NULL;
memset(&hint, 0, sizeof(hint));
hint.ai_family = AF_INET;
int ret = getaddrinfo(pHostName, NULL, &hint, &result);
if(ret==0) {
const struct addrinfo *ai;
ret = -1;
for(ai = result; ai; ai = ai->ai_next) {
assert(ai->ai_family==AF_INET); // ensured by hint
const struct sockaddr_in *answer = (const struct sockaddr_in*)ai->ai_addr;
*pIPA = answer->sin_addr;
ret = 0;
break;
}
}
if(result) {
freeaddrinfo(result);
}
return ret;
}
#endif
+71 -22
View File
@@ -50,6 +50,7 @@
#include "epicsAssert.h"
#include "epicsExit.h"
#include "epicsAtomic.h"
#include "envDefs.h"
LIBCOM_API void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level);
LIBCOM_API void osdThreadHooksRun(epicsThreadId id);
@@ -93,6 +94,7 @@ static pthread_mutex_t listLock;
static ELLLIST pthreadList = ELLLIST_INIT;
static commonAttr *pcommonAttr = 0;
static int epicsThreadOnceCalled = 0;
static int wantPrioScheduling = 0;
static epicsThreadOSD *createImplicit(void);
@@ -119,7 +121,7 @@ if((status)) {\
if(status) { \
fprintf(stderr,"%s error %s",(message),strerror((status))); \
fprintf(stderr," %s\n",method); \
fprintf(stderr,"epicsThreadInit cant proceed. Program exiting\n"); \
fprintf(stderr,"epicsThreadInit can't proceed. Program exiting\n"); \
exit(-1);\
}
@@ -384,6 +386,7 @@ static void once(void)
checkStatusOnce(status,"pthread_attr_getschedparam");
findPriorityRange(pcommonAttr);
envGetBoolConfigParam(&EPICS_ALLOW_POSIX_THREAD_PRIORITY_SCHEDULING, &wantPrioScheduling);
if(pcommonAttr->maxPriority == -1) {
pcommonAttr->maxPriority = pcommonAttr->schedParam.sched_priority;
@@ -396,18 +399,13 @@ static void once(void)
pcommonAttr->maxPriority);
}
if (errVerbose) {
fprintf(stderr, "LRT: min priority: %d max priority %d\n",
pcommonAttr->minPriority, pcommonAttr->maxPriority);
}
#else
if(errVerbose) fprintf(stderr,"task priorities are not implemented\n");
#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */
pthreadInfo = init_threadInfo("_main_",0,epicsThreadGetStackSize(epicsThreadStackSmall),0,0,0);
assert(pthreadInfo!=NULL);
status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo);
status = pthread_setspecific(getpthreadInfo,pthreadInfo);
checkStatusOnceQuit(status,"pthread_setspecific","epicsThreadInit");
status = mutexLock(&listLock);
checkStatusQuit(status,"pthread_mutex_lock","epicsThreadInit");
@@ -427,6 +425,10 @@ static void * start_routine(void *arg)
int status;
sigset_t blockAllSig;
// concurrently written from creator thread with same value
pthreadInfo->tid = pthread_self();
epicsAtomicWriteMemoryBarrier();
sigfillset(&blockAllSig);
pthread_sigmask(SIG_SETMASK,&blockAllSig,NULL);
status = pthread_setspecific(getpthreadInfo,arg);
@@ -459,12 +461,16 @@ static void epicsThreadInit(void)
}
}
static
unsigned char mlocked;
LIBCOM_API
void epicsThreadRealtimeLock(void)
{
mlocked = 0;
#if USE_MEMLOCK
#ifndef RTEMS_LEGACY_STACK // seems to be part of libbsd?
if (pcommonAttr->maxPriority > pcommonAttr->minPriority) {
if (pcommonAttr->maxPriority > pcommonAttr->minPriority && wantPrioScheduling) {
int status = mlockall(MCL_CURRENT | MCL_FUTURE);
if (status) {
@@ -473,19 +479,21 @@ void epicsThreadRealtimeLock(void)
#ifdef __linux__
case ENOMEM:
fprintf(stderr, "epicsThreadRealtimeLock "
"Warning: unable to lock memory. RLIMIT_MEMLOCK is too small or missing CAP_IPC_LOCK\n");
ERL_WARNING ": unable to lock memory. RLIMIT_MEMLOCK is too small or missing CAP_IPC_LOCK\n");
break;
case EPERM:
fprintf(stderr, "epicsThreadRealtimeLock "
"Warning: unable to lock memory. missing CAP_IPC_LOCK\n");
ERL_WARNING ": unable to lock memory. missing CAP_IPC_LOCK\n");
break;
#endif
default:
fprintf(stderr, "epicsThreadRealtimeLock "
"Warning: Unable to lock the virtual address space.\n"
ERL_WARNING ": Unable to lock the virtual address space.\n"
"VM page faults may harm real-time performance. errno=%d\n",
err);
}
} else {
mlocked = 1;
}
}
#endif // LEGACY STACK
@@ -603,22 +611,45 @@ epicsThreadCreateOpt(const char * name,
return 0;
pthreadInfo->isEpicsThread = 1;
setSchedulingPolicy(pthreadInfo, SCHED_FIFO);
pthreadInfo->isRealTimeScheduled = 1;
if (wantPrioScheduling) {
setSchedulingPolicy(pthreadInfo, SCHED_FIFO);
pthreadInfo->isRealTimeScheduled = 1;
}
/* The initial ref. will be transfered to the new thread on success,
* but is retained on error.
* Add a second temporary ref. for this function, in case the new thread
* is created, then ends before pthread_create() returns!.
*/
epicsAtomicIncrIntT(&pthreadInfo->refcnt);
if (pthreadInfo->joinable) {
/* extra ref for epicsThreadMustJoin() */
epicsAtomicIncrIntT(&pthreadInfo->refcnt);
}
status = pthread_create(&pthreadInfo->tid, &pthreadInfo->attr,
pthread_t new_tid;
status = pthread_create(&new_tid, &pthreadInfo->attr,
start_routine, pthreadInfo);
// pthreadInfo->tid concurrently written with same value by new thread
pthreadInfo->tid = new_tid;
epicsAtomicWriteMemoryBarrier();
free_threadInfo(pthreadInfo); // dispose of temp ref
/* On success, pthreadInfo treat as invalid after this point.
* (eg. very short lived thread which self-joins)
* On error, we own all refs.
*/
if (status==EPERM) {
/* Try again without SCHED_FIFO*/
if (pthreadInfo->joinable) {
int cnt = epicsAtomicDecrIntT(&pthreadInfo->refcnt);
assert(cnt==1);
epicsAtomicDecrIntT(&pthreadInfo->refcnt); // dispose of joiner ref.
}
// one ref. left
assert(1==epicsAtomicGetIntT(&pthreadInfo->refcnt));
free_threadInfo(pthreadInfo);
pthreadInfo = init_threadInfo(name, opts->priority, stackSize,
@@ -626,17 +657,28 @@ epicsThreadCreateOpt(const char * name,
if (pthreadInfo==0)
return 0;
epicsAtomicIncrIntT(&pthreadInfo->refcnt); // temp ref
if (pthreadInfo->joinable) {
epicsAtomicIncrIntT(&pthreadInfo->refcnt); // for caller to join
}
pthreadInfo->isEpicsThread = 1;
status = pthread_create(&pthreadInfo->tid, &pthreadInfo->attr,
status = pthread_create(&new_tid, &pthreadInfo->attr,
start_routine, pthreadInfo);
// pthreadInfo->tid concurrently written with same value by new thread
pthreadInfo->tid = new_tid;
epicsAtomicWriteMemoryBarrier();
free_threadInfo(pthreadInfo); // dispose of temp ref
}
checkStatusOnce(status, "pthread_create");
if (status) {
if (pthreadInfo->joinable) {
/* release extra ref which would have been for epicsThreadMustJoin() */
int cnt = epicsAtomicDecrIntT(&pthreadInfo->refcnt);
assert(cnt==1);
epicsAtomicDecrIntT(&pthreadInfo->refcnt); // dispose of joiner ref.
}
// one ref. left
assert(1==epicsAtomicGetIntT(&pthreadInfo->refcnt));
free_threadInfo(pthreadInfo);
return 0;
}
@@ -666,6 +708,8 @@ static epicsThreadOSD *createImplicit(void)
pthreadInfo->tid = tid;
pthreadInfo->osiPriority = 0;
pthreadInfo->isOkToBlock = 1;
status = pthread_attr_init(&pthreadInfo->attr);
checkStatusOnce(status,"pthread_attr_init");
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0
if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) {
@@ -677,7 +721,7 @@ static epicsThreadOSD *createImplicit(void)
}
#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */
status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo);
status = pthread_setspecific(getpthreadInfo,pthreadInfo);
checkStatus(status,"pthread_setspecific createImplicit");
if(status){
free_threadInfo(pthreadInfo);
@@ -980,6 +1024,11 @@ LIBCOM_API void epicsStdCall epicsThreadShowAll(unsigned int level)
}
status = pthread_mutex_unlock(&listLock);
checkStatus(status,"pthread_mutex_unlock epicsThreadShowAll");
fprintf(stderr,
"OSD priority range min: %d max %d, memory %slocked\n",
pcommonAttr->minPriority, pcommonAttr->maxPriority,
mlocked ? "" : "not ");
}
LIBCOM_API void epicsStdCall epicsThreadShow(epicsThreadId showThread, unsigned int level)
@@ -1039,7 +1088,7 @@ LIBCOM_API void epicsStdCall epicsThreadPrivateDelete(epicsThreadPrivateId id)
assert(epicsThreadOnceCalled);
status = pthread_key_delete(*key);
checkStatusQuit(status,"pthread_key_delete","epicsThreadPrivateDelete");
free((void *)key);
free(key);
}
LIBCOM_API void epicsStdCall epicsThreadPrivateSet (epicsThreadPrivateId id, void *value)
@@ -37,7 +37,7 @@ void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level)
if(!status) priority = param.sched_priority;
}
fprintf(epicsGetStdout(),"%16.16s %14p %12lu %3d%8d %8.8s%s\n",
pthreadInfo->name,(void *)
pthreadInfo->name,
pthreadInfo,(unsigned long)pthreadInfo->tid,
pthreadInfo->osiPriority,priority,
pthreadInfo->isSuspended?"SUSPEND":"OK",
@@ -22,10 +22,10 @@
#elif defined ( _BIG_ENDIAN )
# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG
#else
# error EPICS hasnt been ported to byte order specified by <sys/isa_defs.h> on Solaris
# error EPICS has not been ported to byte order specified by <sys/isa_defs.h> on Solaris
#endif
/* for now, assume that Solaris doesnt run on weird arch like ARM NWFP */
/* for now, assume that Solaris doesn't run on weird arch like ARM NWFP */
#define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER
#endif /* ifdef osdWireConfig_h */
@@ -171,7 +171,7 @@ static long vxDevConnectInterruptVME (
return S_dev_vectorInUse;
}
status = intConnect(
(void *)INUM_TO_IVEC(vectorNumber),
INUM_TO_IVEC(vectorNumber),
pFunction,
(int) parameter);
if (status<0) {
@@ -214,7 +214,7 @@ static long vxDevDisconnectInterruptVME (
}
status = intConnect(
(void *)INUM_TO_IVEC(vectorNumber),
INUM_TO_IVEC(vectorNumber),
unsolicitedHandlerEPICS,
(int) vectorNumber);
if(status<0){
@@ -406,7 +406,7 @@ static int vxDevInterruptInUseVME (unsigned vectorNumber)
psub = isrFetch (vectorNumber);
/*
* its a C routine. Does it match a default handler?
* it's a C routine. Does it match a default handler?
*/
for (i=0; i<NELEMENTS(defaultHandlerAddr); i++) {
if (defaultHandlerAddr[i] == psub) {
@@ -87,8 +87,8 @@ EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void)
* is the same as UINT_MAX then sizeof ( atomic_t )
* will be the same as sizeof ( size_t )
*
* if ULONG_MAX != UINT_MAX then its 64 bit vxWorks and
* WRS doesnt not supply at this time the atomic interface
* if ULONG_MAX != UINT_MAX then it's 64 bit vxWorks and
* WRS does not supply at this time the atomic interface
* for 8 byte integers that is needed - so that architecture
* receives the lock synchronized version
*/
@@ -153,7 +153,7 @@ EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta
#else /* ULONG_MAX == UINT_MAX */
/*
* if its 64 bit SMP vxWorks and the compiler doesnt
* if it's 64 bit SMP vxWorks and the compiler doesn't
* have an intrinsic then maybe there isn't any way to
* implement these without using a global lock because
* size_t is maybe bigger than atomic_t
@@ -51,7 +51,7 @@ osdReadlineBegin(struct readlineContext *context)
if (osd->ledId == (LED_ID) ERROR) {
context->in = stdin;
printf("Warning -- Unabled to allocate space for command-line history.\n");
printf("Warning -- Command-line editting disabled.\n");
printf("Warning -- Command-line editing disabled.\n");
}
}
context->osd = osd;
@@ -23,10 +23,10 @@
#elif _BYTE_ORDER == _BIG_ENDIAN
# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG
#else
# error EPICS hasnt been ported to _BYTE_ORDER specified by vxWorks <types/vxArch.h>
# error EPICS has not been ported to _BYTE_ORDER specified by vxWorks <types/vxArch.h>
#endif
/* for now, assume that vxWorks doesnt run on weird arch like ARM NWFP */
/* for now, assume that vxWorks doesn't run on weird arch like ARM NWFP */
#define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER
#endif /* ifdef osdWireConfig_h */
+3 -3
View File
@@ -70,7 +70,7 @@ LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
ifa->ifa_name));
/*
* If its not an internet interface then don't use it
* If it's not an internet interface then don't use it
*/
if ( ifa->ifa_addr->sa_family != AF_INET ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", ifa->ifa_name) );
@@ -88,7 +88,7 @@ LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) {
struct sockaddr_in *pInetAddr = (struct sockaddr_in *) ifa->ifa_addr;
if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", ifa->ifa_name) );
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didn't match\n", ifa->ifa_name) );
continue;
}
}
@@ -207,7 +207,7 @@ static void osiLocalAddrOnce (void *raw)
"osiLocalAddr(): only loopback found\n");
/* fallback to loopback */
osiSockAddr addr;
memset ( (void *) &addr, '\0', sizeof ( addr ) );
memset ( &addr, '\0', sizeof ( addr ) );
addr.ia.sin_family = AF_INET;
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
osiLocalAddrResult = addr;
+4 -4
View File
@@ -135,7 +135,7 @@ LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
(unsigned)current_ifreqsize));
/*
* If its not an internet interface then don't use it
* If it's not an internet interface then don't use it
*/
if ( pIfreqList->ifr_addr.sa_family != AF_INET ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) );
@@ -153,7 +153,7 @@ LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) {
struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr;
if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) );
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didn't match\n", pIfreqList->ifr_name) );
continue;
}
}
@@ -260,7 +260,7 @@ static void osiLocalAddrOnce (void *raw)
struct ifreq *pIfreqListEnd;
struct ifreq *pnextifreq;
memset ( (void *) &addr, '\0', sizeof ( addr ) );
memset ( &addr, '\0', sizeof ( addr ) );
addr.sa.sa_family = AF_UNSPEC;
pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pIfreqList) );
@@ -336,7 +336,7 @@ static void osiLocalAddrOnce (void *raw)
"osiLocalAddr(): only loopback found\n");
fail:
/* fallback to loopback */
memset ( (void *) &addr, '\0', sizeof ( addr ) );
memset ( &addr, '\0', sizeof ( addr ) );
addr.ia.sin_family = AF_INET;
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
osiLocalAddrResult = addr;
+1 -1
View File
@@ -60,7 +60,7 @@ static const iocshArg * const InitArgs[1] = { &InitArg0 };
static const iocshFuncDef InitFuncDef = {
"ClockTime_Init", 1, InitArgs,
"Starts or stops the IOC periodically synchronizing the OS clock\n"
"with the higest priority working time provider.\n"};
"with the highest priority working time provider.\n"};
static void InitCallFunc(const iocshArgBuf *args)
{
ClockTime_Init(args[0].ival);
+1 -1
View File
@@ -292,7 +292,7 @@ int NTPTime_Report(int level)
epicsTimeToStrftime(lastSync, sizeof(lastSync),
"%Y-%m-%d %H:%M:%S.%06f", &NTPTimePvt.syncTime);
printf("Syncronization interval = %.1f seconds\n",
printf("Synchronization interval = %.1f seconds\n",
NTPTimeSyncInterval);
printf("Last synchronized at %s\n",
lastSync);
+2 -2
View File
@@ -114,7 +114,7 @@ LIBCOM_API void epicsStdCall
*
* This enum specifies how to interrupt a blocking socket system call.
* Fortunately, on most systems the combination of a shutdown of both directions
* and/or a signal is sufficent to interrupt a blocking send, receive, or
* and/or a signal is sufficient to interrupt a blocking send, receive, or
* connect call. For odd ball systems this is stubbed out in the osi area.
*
*/
@@ -235,7 +235,7 @@ LIBCOM_API unsigned epicsStdCall ipAddrToDottedIP (
* \param[out] pBuf Pointer to a character buffer where the output string will be placed
* \param bufSize Size of the array pointed to by pBuf
* \return the number of character elements stored in buffer not including the
* null termination. This will be zero if a matching host name cant be found.
* null termination. This will be zero if a matching host name can't be found.
*/
LIBCOM_API unsigned epicsStdCall ipAddrToHostName (
const struct in_addr * pAddr, char * pBuf, unsigned bufSize );
+4 -4
View File
@@ -161,7 +161,7 @@ inline void WireGet ( const epicsUInt8 * pWireSrc, T & dst )
{
// copy through union here
// a) prevents over-aggressive optimization under strict aliasing rules
// b) doesnt preclude extra copy operation being optimized away
// b) doesn't preclude extra copy operation being optimized away
WireAlias < T > tmp;
WireGet ( pWireSrc, tmp._u );
dst = tmp._o;
@@ -198,7 +198,7 @@ inline void WireSet ( const T & src, epicsUInt8 * pWireDst )
{
// copy through union here
// a) prevents over-aggressive optimization under strict aliasing rules
// b) doesnt preclude extra copy operation being optimized away
// b) doesn't preclude extra copy operation being optimized away
WireAlias < T > tmp;
tmp._o = src;
WireSet ( tmp._u, pWireDst );
@@ -234,7 +234,7 @@ inline void AlignedWireGet ( const T & src, T & dst )
{
// copy through union here
// a) prevents over-aggressive optimization under strict aliasing rules
// b) doesnt preclude extra copy operation being optimized away
// b) doesn't preclude extra copy operation being optimized away
WireAlias < T > srcu, dstu;
srcu._o = src;
AlignedWireGet ( srcu._u, dstu._u );
@@ -246,7 +246,7 @@ inline void AlignedWireSet ( const T & src, T & dst )
{
// copy through union here
// a) prevents over-aggressive optimization under strict aliasing rules
// b) doesnt preclude extra copy operation being optimized away
// b) doesn't preclude extra copy operation being optimized away
WireAlias < T > srcu, dstu;
srcu._o = src;
AlignedWireSet ( srcu._u, dstu._u );
+3 -3
View File
@@ -52,7 +52,7 @@ LIBCOM_API epicsRingBytesId epicsStdCall epicsRingBytesCreate(int size)
pring->nextGet = 0;
pring->nextPut = 0;
pring->lock = 0;
return((void *)pring);
return pring;
}
LIBCOM_API epicsRingBytesId epicsStdCall epicsRingBytesLockedCreate(int size)
@@ -61,14 +61,14 @@ LIBCOM_API epicsRingBytesId epicsStdCall epicsRingBytesLockedCreate(int size)
if(!pring)
return NULL;
pring->lock = epicsSpinCreate();
return((void *)pring);
return pring;
}
LIBCOM_API void epicsStdCall epicsRingBytesDelete(epicsRingBytesId id)
{
ringPvt *pring = (ringPvt *)id;
if (pring->lock) epicsSpinDestroy(pring->lock);
free((void *)pring);
free(pring);
}
LIBCOM_API int epicsStdCall epicsRingBytesGet(
+10 -10
View File
@@ -112,7 +112,7 @@ static void twdTask(void *arg)
char tName[40];
epicsThreadGetName(pt->tid, tName, sizeof(tName));
errlogPrintf("Thread %s (%p) suspended\n",
tName, (void *)pt->tid);
tName, pt->tid);
if (pt->callback) {
pt->callback(pt->usr);
}
@@ -200,7 +200,7 @@ void taskwdInsert(epicsThreadId tid, TASKWDFUNC callback, void *usr)
epicsMutexUnlock(mLock);
epicsMutexMustLock(tLock);
ellAdd(&tList, (void *)pt);
ellAdd(&tList, &pt->node);
epicsMutexUnlock(tLock);
}
@@ -219,7 +219,7 @@ void taskwdRemove(epicsThreadId tid)
pt = (struct tNode *)ellFirst(&tList);
while (pt != NULL) {
if (tid == pt->tid) {
ellDelete(&tList, (void *)pt);
ellDelete(&tList, &pt->node);
epicsMutexUnlock(tLock);
freeNode((union twdNode *)pt);
@@ -240,7 +240,7 @@ void taskwdRemove(epicsThreadId tid)
epicsThreadGetName(tid, tName, sizeof(tName));
errlogPrintf("taskwdRemove: Thread %s (%p) not registered!\n",
tName, (void *)tid);
tName, tid);
}
@@ -259,7 +259,7 @@ void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr)
pm->usr = usr;
epicsMutexMustLock(mLock);
ellAdd(&mList, (void *)pm);
ellAdd(&mList, &pm->node);
epicsMutexUnlock(mLock);
}
@@ -275,7 +275,7 @@ void taskwdMonitorDel(const taskwdMonitor *funcs, void *usr)
pm = (struct mNode *)ellFirst(&mList);
while (pm) {
if (pm->funcs == funcs && pm->usr == usr) {
ellDelete(&mList, (void *)pm);
ellDelete(&mList, &pm->node);
freeNode((union twdNode *)pm);
epicsMutexUnlock(mLock);
return;
@@ -322,7 +322,7 @@ void taskwdAnyInsert(void *key, TASKWDANYFUNC callback, void *usr)
pm->usr = pa;
epicsMutexMustLock(mLock);
ellAdd(&mList, (void *)pm);
ellAdd(&mList, &pm->node);
epicsMutexUnlock(mLock);
}
@@ -339,7 +339,7 @@ void taskwdAnyRemove(void *key)
if (pm->funcs == &anyFuncs) {
pa = (struct aNode *)pm->usr;
if (pa->key == key) {
ellDelete(&mList, (void *)pm);
ellDelete(&mList, &pm->node);
freeNode((union twdNode *)pa);
freeNode((union twdNode *)pm);
epicsMutexUnlock(mLock);
@@ -382,7 +382,7 @@ LIBCOM_API void taskwdShow(int level)
epicsThreadGetName(pt->tid, tName, sizeof(tName));
printf("%16.16s %9s %12p %12p %12p\n",
tName, pt->suspended ? "Suspended" : "Ok ",
(void *)pt->tid, (void *)pt->callback, pt->usr);
pt->tid, pt->callback, pt->usr);
pt = (struct tNode *)ellNext(&pt->node);
}
}
@@ -425,6 +425,6 @@ static void freeNode(union twdNode *pn)
VALGRIND_MEMPOOL_FREE(&fList, pn);
VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(ELLNODE));
epicsMutexMustLock(fLock);
ellAdd(&fList, (void *)pn);
ellAdd(&fList, &pn->t.node);
epicsMutexUnlock(fLock);
}
+5 -1
View File
@@ -65,7 +65,11 @@ void timer::start ( epicsTimerNotify & notify, const epicsTime & expire )
void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire )
{
this->pNotify = & notify;
this->exp = expire - ( this->queue.notify.quantum () / 2.0 );
this->exp = expire
#ifdef TIMER_QUANTUM_BIAS
- ( this->queue.notify.quantum () / 2.0 )
#endif
;
bool reschedualNeeded = false;
if ( this->curState == stateActive ) {
+10
View File
@@ -35,6 +35,16 @@
# define debugPrintf(ARGSINPAREN)
#endif
/* For historical reasons, round down expiration time on vxWorks and RTEMS.
* Targets with a fixed timer period.
* This biasing down by half a period allows timers which expire on every tick.
* Otherwise, the fastest timer is 2x the tick period.
* This results in timers expiring one period earlier than requested.
*/
#if defined(vxWorks) || defined(__rtems__)
# define TIMER_QUANTUM_BIAS 1
#endif
template < class T > class epicsGuard;
class timer : public epicsTimer, public tsDLNode < timer > {
+1 -1
View File
@@ -148,7 +148,7 @@ double timerQueue::process ( const epicsTime & currentTime )
}
//
// only restart if they didnt cancel() the timer
// only restart if they didn't cancel() the timer
// while the call back was running
//
if ( this->cancelPending ) {
+1 -1
View File
@@ -41,7 +41,7 @@ static int yyerror(char *str)
The FirstFlag and yyrestart jazz is the flex-flavored version of how you
restart the scanner if you want to parse another file. Without it, the
scanner can only be used one time. Even if you think your code will only
be used one time, it is desireable to put in the restart stuff anyway,
be used one time, it is desirable to put in the restart stuff anyway,
because some day you (or your boss) will change your mind and without it
the IOC will crash when you make your second call to your parser.
+1 -1
View File
@@ -35,7 +35,7 @@ is
%ident string
where string is a sequence of characters begining with a double quote
where string is a sequence of characters beginning with a double quote
and ending with either a double quote or the next end-of-line, whichever
comes first. The declaration will cause a #ident directive to be written
near the start of the output file.
+1 -1
View File
@@ -67,7 +67,7 @@ yajl_alloc(const yajl_callbacks * callbacks,
}
/* copy in pointers to allocation routines */
memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
memcpy(&(hand->alloc), afs, sizeof(yajl_alloc_funcs));
hand->callbacks = callbacks;
hand->ctx = ctx;
+1 -1
View File
@@ -60,7 +60,7 @@ yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc)
return NULL;
}
memset((void *) b, 0, sizeof(struct yajl_buf_t));
memset(b, 0, sizeof(struct yajl_buf_t));
b->alloc = alloc;
return b;
}
+1 -1
View File
@@ -54,7 +54,7 @@ typedef struct yajl_bytestack_t
if (((obs).size - (obs).used) == 0) { \
(obs).size += YAJL_BS_INC; \
(obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\
(void *) (obs).stack, (obs).size);\
(obs).stack, (obs).size);\
} \
(obs).stack[((obs).used)++] = (byte); \
}
+3 -3
View File
@@ -115,9 +115,9 @@ yajl_gen_alloc(const yajl_alloc_funcs * afs)
g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t));
if (!g) return NULL;
memset((void *) g, 0, sizeof(struct yajl_gen_t));
memset(g, 0, sizeof(struct yajl_gen_t));
/* copy in pointers to allocation routines */
memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
memcpy(&(g->alloc), afs, sizeof(yajl_alloc_funcs));
g->print = (yajl_print_t)&yajl_buf_append;
g->ctx = yajl_buf_alloc(&(g->alloc));
@@ -130,7 +130,7 @@ void
yajl_gen_reset(yajl_gen g, const char * sep)
{
g->depth = 0;
memset((void *) &(g->state), 0, sizeof(g->state));
memset(&(g->state), 0, sizeof(g->state));
if (sep != NULL) g->print(g->ctx, sep, strlen(sep));
}
+3 -3
View File
@@ -113,7 +113,7 @@ yajl_lex_alloc(yajl_alloc_funcs * alloc,
return NULL;
}
memset((void *) lxr, 0, sizeof(struct yajl_lexer_t));
memset(lxr, 0, sizeof(struct yajl_lexer_t));
lxr->buf = yajl_buf_alloc(alloc);
lxr->allowComments = allowComments;
lxr->validateUTF8 = validateUTF8;
@@ -389,7 +389,7 @@ yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText,
/* accept it, and move on */
}
finish_string_lex:
/* tell our buddy, the parser, wether he needs to process this string
/* tell our buddy, the parser, whether he needs to process this string
* again */
if (hasEscapes && tok == yajl_tok_string) {
tok = yajl_tok_string_with_escapes;
@@ -725,7 +725,7 @@ yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
* - malformed comment opening (slash not followed by
* '*' or '/') (tok_error)
* - eof hit. (tok_eof) */
tok = yajl_lex_comment(lexer, (const unsigned char *) jsonText,
tok = yajl_lex_comment(lexer, jsonText,
jsonTextLen, offset);
if (tok == yajl_tok_comment) {
/* "error" is silly, but that's the initial
+3 -3
View File
@@ -34,7 +34,7 @@ extern "C" {
yajl_status_ok,
/** A client callback returned zero, stopping the parse. */
yajl_status_client_canceled,
/** An error occured during the parse. Call yajl_get_error() for
/** An error occurred during the parse. Call yajl_get_error() for
* more information about the encountered error. */
yajl_status_error
} yajl_status;
@@ -229,7 +229,7 @@ extern "C" {
/** Get an error string describing the state of the parse.
*
* If verbose is non-zero, the message will include the JSON text where
* the error occured, along with an arrow pointing to the specific char.
* the error occurred, along with an arrow pointing to the specific char.
*
* \returns A dynamically allocated string will be returned which should
* be freed with yajl_free_error().
@@ -246,7 +246,7 @@ extern "C" {
*
* In the event an error is encountered during parsing, this function
* affords the client a way to get the offset into the most recent
* chunk where the error occured. 0 will be returned if no error
* chunk where the error occurred. 0 will be returned if no error
* was encountered.
*/
YAJL_API size_t yajl_get_bytes_consumed(yajl_handle hand);