[Search for users]
[Overall Top Noters]
[List of all Conferences]
[Download this site]
Title: | DECC |
Notice: | General DEC C discussions |
Moderator: | TLE::D_SMITH N TE |
|
Created: | Fri Nov 13 1992 |
Last Modified: | Fri Jun 06 1997 |
Last Successful Update: | Fri Jun 06 1997 |
Number of topics: | 2212 |
Total number of notes: | 11045 |
2119.0. "Quadword granularity and shared data?" by CSC32::J_HENSON (Don't get even, get ahead!) Wed Mar 12 1997 13:47
Dec C V5.5-002, OpenVMS V6.2, Alpha
I'm a little bit confused as to what quadword granularity is all about,
and I have a customer who is maintaining that it's not working correctly.
I need a little help understanding this so that I can properly deal
with this customer. And, if he is correct, then I need to report
what appears to be a bug in the compiler.
See the example (it's sort of lengthy) at the bottom for details of
what the customer is doing.
In a nutshell, though, the customer is creating a shared global section
(via calls to $crempsc and $mgblsc), and associating a structure
pointer to the section. He then runs two independent programs, one
which repeatedly reads a quadword, and another that repeatedly
updates the same quadword. In both program, he uses a pointer to
access the shared memory.
The customer's contention is that quadword granularity should be all that
is needed to prevent data corruption between the two competing processes.
However, when you run his code, you see that the shared memory does indeed
get corrupted.
Am I, and the customer, missing something here, or is this indeed a
compiler bug?
Thanks,
Jerry
==================================================================================
$create proto.h
int delvirspc(void *startp, int size);
int pagesize(void);
char *attachglb(char *name, char glbtype, char rwflag);
char *createglb(char *glbfilname, char *glbsecnae, char glbtype, int size);
int deleteglb(char *name, char glbtype);
void waitms(int msec);
$!
$create test.h
#define TEST_VAL1 (123456789L)
#define TEST_VAL2 (272727272L)
#define TEST_STRUCT_FILE "test_global.glb"
#define TEST_STRUCT_NAME "TEST_GLOBAL"
#define BLOCK_BYTES 512 /* disk block size in bytes */
typedef signed __int64 natural;
typedef unsigned __int64 unatural;
#define SUCCESS 1
#define FAILURE 0
#pragma member_alignment save
#pragma member_alignment
typedef struct
{
unatural field1;
unatural flag1;
unatural field2;
}TEST_STRUCT;
#pragma member_alignment restore
$!
$create vms.h
unsigned int SYS$ADJWSL();
unsigned int SYS$ASSIGN();
unsigned int SYS$CANWAK();
unsigned int SYS$CRELNM();
unsigned int SYS$CREMBX();
unsigned int SYS$CREPRC();
unsigned int SYS$CRMPSC();
unsigned int SYS$DASSGN();
unsigned int SYS$DGBLSC();
unsigned int SYS$DELMBX();
unsigned int SYS$DELPRC();
unsigned int SYS$DELTVA();
unsigned int SYS$DISMOU();
unsigned int SYS$GETDVIW();
unsigned int SYS$GETJPIW();
unsigned int SYS$GETMSG();
unsigned int SYS$GETTIM();
unsigned int SYS$HIBER();
unsigned int SYS$LCKPAG();
unsigned int SYS$LKWSET();
unsigned int SYS$MGBLSC();
unsigned int SYS$MOUNT();
unsigned int SYS$NUMTIM();
unsigned int SYS$QIO();
unsigned int SYS$QIOW();
unsigned int SYS$SCHDWK();
unsigned int SYS$SETIMR();
unsigned int SYS$SNDJBCW();
unsigned int SYS$SYNCH();
unsigned int SYS$WAKE();
unsigned int SYS$CREATE();
unsigned int SYS$OPEN();
unsigned int SYS$CONNECT();
unsigned int SYS$DELETE();
unsigned int SYS$FLUSH();
unsigned int SYS$REWIND();
unsigned int SYS$FIND();
unsigned int SYS$GET();
unsigned int SYS$PARSE();
unsigned int SYS$PUT();
unsigned int SYS$UPDATE();
unsigned int SYS$CLOSE();
$!
$create attachglb.c
#include <stdio.h>
#include <string.h>
#include <ssdef.h>
#include <secdef.h>
#include <descrip.h>
#include "vms.h"
#include "test.h"
#include "proto.h"
char *attachglb(char *name, /* global section name */
char glbtype, /* type of global section */
char rwflag) /* global section access mode */
{
unsigned int attachsts;
unsigned int flags;
unsigned int inadr[2];
unsigned int retadr[2];
struct dsc$descriptor_s logicalname;
/* starting and ending addresses to be mapped into */
inadr[0] = 0;
if (rwflag != 'r' && rwflag != 'R' && rwflag != 'w' && rwflag != 'W')
{
printf("bad parameter for mapping global section access mode\n");
return(0);
}
if (rwflag == 'r' || rwflag == 'R')
{
/* map (system) global section with read-only access */
flags = SEC$M_EXPREG;
}
else
{
/* map (system) global section with read/write access */
flags = SEC$M_WRT | SEC$M_EXPREG ;
}
if (glbtype == 'S' || glbtype == 's')
flags = flags | SEC$M_SYSGBL;
logicalname.dsc$w_length = strlen(name);
logicalname.dsc$b_dtype = DSC$K_DTYPE_T;
logicalname.dsc$b_class = DSC$K_CLASS_S;
logicalname.dsc$a_pointer = name;
attachsts = SYS$MGBLSC(inadr, retadr, 0, flags, &logicalname, 0, 0);
if (attachsts != SS$_NORMAL)
{
printf("(attachsts = SYS$MGBLSC) != SS$_NORMAL\n");
return(0);
}
return((char *)retadr[0]);
}
$!
$create createglb.c
#include <stdio.h>
#include <string.h>
#include <ssdef.h>
#include <secdef.h>
#include <rms.h>
#include <descrip.h>
#include "vms.h"
#include "test.h"
#include "proto.h"
char *createglb(char *glbfilname, /* physical file name */
char *glbsecname, /* global section name */
char glbtype, /* type of global section */
int size) /* global section size */
{
struct FAB fblock; /* File Access Block */
unsigned int rmssts; /* Record Management Services status */
unsigned int inadr[2];
unsigned int retadr[2];
unsigned int creatests;
unsigned int flags;
unsigned int chan; /* channel number to access file */
unsigned int attachsts;
int existing_size;
int page_size;
int num_pages;
struct dsc$descriptor_s logicalname;
if (size <= 0)
{
printf("size <= 0\n");
return(0);
}
/* starting and ending addresses to map */
inadr[0] = 0;
flags = SEC$M_WRT | SEC$M_EXPREG ;
logicalname.dsc$w_length = strlen(glbsecname);
logicalname.dsc$b_dtype = DSC$K_DTYPE_T;
logicalname.dsc$b_class = DSC$K_CLASS_S;
logicalname.dsc$a_pointer = glbsecname;
attachsts = SYS$MGBLSC(inadr, retadr, 0, flags, &logicalname, 0, 0);
if (attachsts == SS$_NORMAL)
{
existing_size = retadr[1] - retadr[0] + 1;
if( existing_size >= size) {
memset( (void *)(retadr[0]), 0, size);
return((char *)retadr[0]);
}
if (delvirspc( (char *)retadr[0], existing_size) == FAILURE)
{
printf("delvirspc() returned FAILURE\n");
return 0;
}
}
if (deleteglb(glbsecname, 'G') == FAILURE)
{
printf("deleteglb failed\n");
}
page_size = pagesize();
if (page_size == 0)
{
return 0;
}
num_pages = ( size + page_size - 1) / page_size;
/* initialize file access block */
fblock = cc$rms_fab;
fblock.fab$l_alq = (num_pages * page_size + BLOCK_BYTES - 1)/BLOCK_BYTES;
fblock.fab$w_ifi = 0;
fblock.fab$l_fop = FAB$M_UFO | FAB$M_CBT;
fblock.fab$l_fna = glbsecname;
fblock.fab$b_fns = strlen(glbsecname);
fblock.fab$b_fac = FAB$M_DEL | FAB$M_GET | FAB$M_PUT | FAB$M_UPD;
fblock.fab$b_shr = FAB$M_NIL;
/* create RMS file */
rmssts = SYS$CREATE(&fblock);
if (rmssts != RMS$_NORMAL && rmssts != RMS$_CREATED)
{
printf("SYS$CREATE(&fblock) failed\n");
return(0);
}
chan = fblock.fab$l_stv;
/* find first slto in p0 space */
inadr[0] = 0;
/* read/write and permanent global section*/
flags = SEC$M_GBL | SEC$M_WRT | SEC$M_PERM | SEC$M_EXPREG | SEC$M_DZRO;
logicalname.dsc$w_length = strlen(glbsecname);
logicalname.dsc$b_dtype = DSC$K_DTYPE_T;
logicalname.dsc$b_class = DSC$K_CLASS_S;
logicalname.dsc$a_pointer = glbsecname;
/* create global section */
creatests = SYS$CRMPSC(inadr, retadr, 0, flags, &logicalname, 0, 0,
chan, 0, 0, 0, 0);
if (creatests != SS$_NORMAL && creatests != SS$_CREATED)
{
printf("SYS$CRMPSC failed\n");
return(0);
}
/* demand zero is not enough if pages not
referenced by THIS task */
memset( (void *)(retadr[0]), 0, size);
return((char *)retadr[0]);
}
$!
$create deleteglb.c
#include <ssdef.h>
#include <secdef.h>
#include <string.h>
#include <descrip.h>
#include "vms.h"
#include "test.h"
#include "proto.h"
int deleteglb(char *name, /* global section name */
char glbtype) /* type of global section */
{
unsigned int deletests;
unsigned int flag;
struct dsc$descriptor_s logicalname;
flag = 0;
if (glbtype == 'S' || glbtype == 's')
flag = SEC$M_SYSGBL;
logicalname.dsc$w_length = strlen(name);
logicalname.dsc$b_dtype = DSC$K_DTYPE_T;
logicalname.dsc$b_class = DSC$K_CLASS_S;
logicalname.dsc$a_pointer = name;
deletests = SYS$DGBLSC(flag, &logicalname, 0);
if (deletests != SS$_NORMAL)
{
return(FAILURE);
}
return;
}
$create delvirspc.c
/* Description:
Delete a range of addresses from a process's virtual address space.
It only deletes a number of PAGES in the process's virtual address
space. Thus, the starting address of the range must be page aligned,
and the size of space to be deleted must be a multiple of pages
*/
#include <stdio.h>
#include <ssdef.h>
#include <secdef.h>
#include <descrip.h>
#include "vms.h"
#include "test.h"
#include "proto.h"
int delvirspc(void *startp, /* start address */
int size) /* size in byte */
{
unsigned int deletests;
unsigned int inadr[2];
unsigned int retadr[2];
int page_size;
page_size = pagesize();
if (page_size == 0) {
return(FAILURE);
}
/*
** make sure size is multiple of pages and starting address is
** page aligned
*/
if (size <= 0)
{
printf("invalid virtual address size\n");
return;
}
if ((int)startp % page_size)
{
printf("starting address, page not aligned\n");
return;
}
inadr[0] = (unsigned int)startp;
inadr[1] = inadr[0] + size - 1;
retadr[0] = 0;
retadr[1] = 0;
deletests = SYS$DELTVA(inadr, retadr, 0);
if (deletests != SS$_NORMAL)
{
printf("failed to delete VIRTUAL SPACE\n");
return;
}
return;
}
$create mtnrast.c
#include <stdlib.h>
#include <ssdef.h>
#include "vms.h"
#include "test.h"
static int waiting;
/*
** AST subroutine:
** - sets the waiting flag to 0
** - wakes the task
*/
static void wtmrast(int tmrid)
{
waiting = 0;
SYS$WAKE( 0, 0);
}
$!
$create pagesize.c
#include <stdio.h>
#include <syidef.h>
#include <ssdef.h>
#include "test.h"
#include "proto.h"
unsigned int SYS$GETSYIW();
#ifndef SYI$_PAGE_SIZE
#define SYI$_PAGE_SIZE 4452
#endif
struct itm
{
short int buflen;
short int item_code;
int bufadr;
int retlenadr;
};
int pagesize()
{
int cpu_pagesize, cpu_pagesize_len; /* alpha */
int status;
struct itm itmlst[2]; /* alpha */
itmlst[0].buflen = sizeof(cpu_pagesize);
itmlst[0].item_code = SYI$_PAGE_SIZE;
itmlst[0].bufadr = (int)&cpu_pagesize;
itmlst[0].retlenadr = (int)&cpu_pagesize_len;
itmlst[1].buflen = 0;
itmlst[1].item_code = 0;
status = SYS$GETSYIW(0, 0, 0, itmlst, 0, 0, 0);
if (status != SS$_NORMAL)
{
printf("(status = SYS$GETSYIW()) != SS$_NORMAL\n");
return(0);
}
return cpu_pagesize;
}
$!
$create setup.c
#include <stdio.h>
#include <stdlib.h>
#include "test.h"
#include "proto.h"
main()
{
int status = SUCCESS;
TEST_STRUCT *test_p;
test_p = (TEST_STRUCT *)createglb(TEST_STRUCT_FILE, TEST_STRUCT_NAME,
'g', sizeof(TEST_STRUCT));
if (test_p == 0)
{
printf("createglb() failed\n");
status = FAILURE;
goto EXIT;
}
test_p->flag1 = TEST_VAL1;
EXIT:
if (status == FAILURE)
{
exit(-1);
}
else
{
exit(0);
}
return;
}
$create task1.c
#include <stdio.h>
#include <stdlib.h>
#include "test.h"
#include "proto.h"
main()
{
char tmpstr[200];
int count=0, status = SUCCESS;
TEST_STRUCT *test_p;
test_p = (TEST_STRUCT *)attachglb(TEST_STRUCT_NAME, 'g', 'w');
if (test_p == 0)
{
printf("attachglb() failed/n");
status = FAILURE;
goto EXIT;
}
printf("addr field1 %d addr field2 %d addr flag1 %d\n",
&test_p->field1, &test_p->field2, &test_p->flag1);
while(1)
{
if ((test_p->flag1 != TEST_VAL1) && (test_p->flag1 != TEST_VAL2))
{
printf("mismatch %d at %d\n", test_p->flag1, count);
goto EXIT;
}
count++;
}
EXIT:
if (status == FAILURE)
{
exit(-1);
}
else
{
exit(0);
}
return;
}
$create task2.c
#include <stdio.h>
#include <stdlib.h>
#include "test.h"
#include "proto.h"
main()
{
char tmpstr[200];
int status = SUCCESS;
TEST_STRUCT *test_p;
test_p = (TEST_STRUCT *)attachglb(TEST_STRUCT_NAME, 'g', 'w');
if (test_p == 0)
{
printf("attachglb() failed/n");
status = FAILURE;
goto EXIT;
}
if (test_p->flag1 != TEST_VAL1 && test_p->flag1 != TEST_VAL2)
{
printf("test_p->flag1 != TEST_VAL1 && test_p->flag1 != TEST_VAL\n");
status = FAILURE;
goto EXIT;
}
while(1)
{
if (test_p->flag1 == TEST_VAL1)
{
test_p->flag1 = TEST_VAL2;
}
else
{
test_p->flag1 = TEST_VAL1;
}
}
EXIT:
if (status == FAILURE)
{
exit(-1);
}
else
{
exit(0);
}
return;
}
$create waitms.c
#include <stdlib.h>
#include <ssdef.h>
#include "vms.h"
#include "test.h"
static int waiting;
/*
** AST subroutine:
** - sets the waiting flag to 0
** - wakes the task
*/
static void wtmrast(int tmrid)
{
waiting = 0;
SYS$WAKE( 0, 0);
}
void waitms(int msec) /* number of milliseconds to sleep for */
{
int timarg[2];
/* convert msec to (negative) 100 nanosecs
NOTE: only one word of the 64 bit time argument is converted
this limits the millisecond argument to less than 429495 msec */
timarg[0] = -10000*msec;
timarg[1] = 0xFFFFFFFF;
waiting = 1;
if (SYS$SETIMR(0,timarg,wtmrast,0,0) != SS$_NORMAL) {
exit(0);
}
while (waiting) {
SYS$HIBER();
}
}
$!
$create task1.run
$! gampro.run
$!
$set process/priv=all
$ SET PROCESS/NAME=testtask1
$ run user1:[j_henson.granularity]task1/nodebug
$ exit
$!
$create task2.run
$! gampro.run
$!
$ SET PROCESS/NAME=testtask2
$ run user1:[j_henson.granularity]task2/nodebug
$ exit
$!
$create setup.lnk
$ link 'p1' /exe=setup.exe -
setup,-
attachglb,-
deleteglb,-
pagesize,-
delvirspc,-
createglb,-
SYS$LIBRARY:vaxcrtl.olb/lib
$!
$create task1.lnk
$ link 'p1' /exe=task1.exe -
task1,-
attachglb,-
deleteglb,-
pagesize,-
delvirspc,-
createglb,-
waitms,-
SYS$LIBRARY:vaxcrtl.olb/lib
$create task2.lnk
$ link 'p1' /exe=task2.exe -
task2,-
attachglb,-
deleteglb,-
pagesize,-
delvirspc,-
createglb,-
waitms,-
SYS$LIBRARY:vaxcrtl.olb/lib
$!
$create build.com
$! BUILD.COM
$!
$! generate the PROD part of the system
$!
$ WRITE SYS$OUTPUT "### BUILD.COM ### ",F$TIME()
$!
$ USER_DIRECTORY = F$DIRECTORY()
$!
$! create a log file
$!
$ PURGE BUILD.LOG
$ DEFINE SYS$OUTPUT BUILD.LOG
$ SET NOVERIFY
$ WRITE SYS$OUTPUT "### BUILD.COM ### ",F$TIME()
$!
$! cleanup
$!
$ WRITE SYS$OUTPUT " "
$ WRITE SYS$OUTPUT ">>> CLEANUP ",F$TIME()
$ SET NOON
$ IF F$SEARCH("*.MAP") .NES. "" THEN DELETE *.MAP;*
$ IF F$SEARCH("*.LIS") .NES. "" THEN DELETE *.LIS;*
$ IF F$SEARCH("*.OBJ") .NES. "" THEN DELETE *.OBJ;*
$ IF F$SEARCH("*.EXE") .NES. "" THEN DELETE *.EXE;*
$!
$ WRITE SYS$OUTPUT " "
$ WRITE SYS$OUTPUT ">>> COMPILING (*.C) ",F$TIME()
$ SET NOON
$!
$ MYCC :== CC/DECC/noopt/lis/machine/STAND=RELAXED_ANSI89/FLOAT=IEEE_FLOAT
$!
$ CCLOOP:
$ FILE = F$SEARCH("*.C;0")
$ IF FILE .EQS. "" THEN GOTO CCEXIT
$ WRITE SYS$OUTPUT "Compiling ",FILE
$ mycc 'FILE'
$ GOTO CCLOOP
$ CCEXIT:
$!
$ WRITE SYS$OUTPUT ">>> LINKING TASKS (*.LNK) ",F$TIME()
$ LNKLOOP:
$ FILE = F$SEARCH("*.LNK;0")
$ IF FILE .EQS. "" THEN GOTO LNKEXIT
$ WRITE SYS$OUTPUT "Linking ",FILE
$ @'FILE' "/NOMAP
$! @'FILE' "/NOMAP/DEBUG
$ GOTO LNKLOOP
$ LNKEXIT:
$!
$!
$create kill.com
$! KILL.COM
$!
$! Kills all tasks in the batch queue associated with test
$!
$ WRITE SYS$OUTPUT F$TIME()," ### KILL.COM (test) ###"
$ WRITE SYS$OUTPUT " "
$!
$ SET NOON
$ IF F$GETQUI("DISPLAY_QUEUE","QUEUE_NAME","testQUEUE","WILDCARD") .EQS.""
$ THEN
$ EXIT
$ ENDIF
$!
$ LOOP:
$ ENTRY = F$GETQUI("DISPLAY_JOB","ENTRY_NUMBER",,"ALL_JOBS")
$ IF ENTRY .NES. ""
$ THEN
$ STOP/QUE testQUEUE/ENTRY= 'ENTRY'
$ WAIT 00:00:01
$ GOTO LOOP
$ ENDIF
$ EXIT
$!
$create start.com
$! START.COM
$!
$! startup of test on base node
$!
$ WRITE SYS$OUTPUT F$TIME()," ### START.COM (test) ###"
$ WRITE SYS$OUTPUT " "
$!
$ SET NOON
$!
$ RUN SETUP/nodebug
$ IF $SEVERITY .NE. 1
$ THEN
$ WRITE SYS$OUTPUT "ABNORMAL EXIT FROM test SETUP"
$ EXIT 42
$ ENDIF
$!
$ IF F$GETQUI("DISPLAY_QUEUE","QUEUE_NAME","testQUEUE") .EQS. ""
$ THEN
$ INITIALIZE/QUEUE/JOB_LIMIT=10/BATCH/BASE_PRIORITY=6 testQUEUE
$ ENDIF
$!
$ IF F$GETQUI("DISPLAY_QUEUE", "QUEUE_STOPPED", "testQUEUE") .EQS. "TRUE"
$ THEN
$ START/QUEUE testQUEUE
$ ENDIF
$!
$ SUBMIT/QUEUE=testQUEUE/NOPRINTER/NOIDENTIFY/LOG_FILE=task1.log -
user1:[j_henson.granularity]TASK1.RUN
$ SUBMIT/QUEUE=testQUEUE/NOPRINTER/NOIDENTIFY/LOG_FILE=task2.log -
user1:[j_henson.granularity]TASK2.RUN
$ EXIT
$@build
$@start
T.R | Title | User | Personal Name | Date | Lines |
---|
2119.1 | Flaw in your testing method | WIBBIN::NOYCE | Pulling weeds, pickin' stones | Wed Mar 12 1997 14:22 | 27 |
| > if ((test_p->flag1 != TEST_VAL1) && (test_p->flag1 != TEST_VAL2))
One legal way for this statement to be executed is as follows:
1. Fetch test_p->flag1.
2. If it's equal to TEST_VAL1, exit the 'if' statement.
3. Fetch test_p->flag1.
4. If it's equal to TEST_VAL2, exit the 'if' statement.
5. Fall into the 'printf'
Compiling with /NOOPT makes it very likely that the pointer will be fetched
twice, as shown here.
Now, what happens if p->flag1 is initially TEST_VAL2, and you start
executing this code -- but the other process executes p->flag1=TEST_VAL1
while you're between steps 1 and 3? The first test will find that the
flag is not VAL1, and the second test will find that the (new value of the)
flag is not VAL2.
I think you need to write this test a bit more carefully:
unatural sample;
:
sample = test_p->flag1;
if ((sample != TEST_VAL1) && (sample != TEST_VAL2))
{ something bad happened }
|
2119.2 | | GEMEVN::GLOSSOP | Only the paranoid survive | Wed Mar 12 1997 14:29 | 6 |
| As Bill stated, in this example, granularity isn't the issue.
The document:
ftp://smop.zko.dec.com/users/glossop/public/atom-g-v.ppt or .ps
provides some additional information on granularity and atomicity,
though it probably wouldn't have answered this particular question.
|
2119.3 | Storage that's changing should be marked volatile... | GEMEVN::GLOSSOP | Only the paranoid survive | Wed Mar 12 1997 14:31 | 4 |
| Also, if you're reading things that are dynamically changing,
you should really be declaring the storage volatile.
(With optimization, non-volatile references might be moved out
of loops, etc.)
|
2119.4 | thanks | CSC32::J_HENSON | Don't get even, get ahead! | Thu Mar 13 1997 09:57 | 19 |
| >> <<< Note 2119.1 by WIBBIN::NOYCE "Pulling weeds, pickin' stones" >>>
>> -< Flaw in your testing method >-
>>I think you need to write this test a bit more carefully:
>>
>> unatural sample;
>> :
>> sample = test_p->flag1;
>> if ((sample != TEST_VAL1) && (sample != TEST_VAL2))
>> { something bad happened }
Thanks for all of the good replies. I tried the recommendation cited
above, and no longer see the 'problem'. I will pass this on to the
customer who provided the sample.
Thanks,
Jerry
|