I'm reading the book Multithreading Applications in Win32
The book says return node->next
will be compiled into separate machine instructions that would not be executed as an atomic operation, so Next()
should also be protected by the critical section.
My question is, what instructions could it be translated into, to cause a race condition?
typedef struct _Node
{
struct Node *next;
int data;
} Node;
typedef struct _List
{
Node *head;
CRITICAL SECTION critical_sec;
} List;
List *CreateList()
{
List *pList = malloc(sizeof(List));
pList->head = NULL;
InitializeCriticalSection(&pList->critical_sec);
return pList;
}
void DeleteList(List *pList)
{
DeleteCriticalSection(&pList->critical_sec);
free(pList);
}
void AddHead(List *pList, Node *node)
{
EnterCriticalSection(&pList->critical_sec);
node->next = pList->head;
pList->head = node;
LeaveCriticalSection(&pList->critical_sec);
}
void Insert(List *pList, Node *afterNode, Node *newNode)
{
EnterCriticalSection(&pList->critical_sec);
if (afterNode == NULL)
{
AddHead(pList, newNode);
}
else
{
newNode->next = afterNode->next;
afterNode->next = newNode;
}
LeaveCriticalSection(&pList->critical_sec);
}
Node *Next(List *pList, Node *node)
{
Node* next;
EnterCriticalSection(&pList->critical_sec);
next = node->next;
LeaveCriticalSection(&pList->critical_sec);
return next;
}
Edit:
OK, although in this particular case it won't corrupt the singly linked list w/o protecting the Next()
operation, a shared structure should be protected as a whole or nothing, generally.
return node->next
performs two operations; it first loads the struct
pointed to by node
into memory, then looks at the node+offsetof(next)
to find the pointer next
, load that into a register, and then return to the calling program. The contents of node
may be manipulated by another thread of execution in the meantime.
... HOWEVER ...
"Writers" (like add or remove node) MUST be mutually exclusive. Critical section is a good choice.
"Readers" (like "next") can run concurrently with each other.
SUGGESTION:
If you can target Windows Vista or higher, consider using an SRW lock instead:
While I think the answer by sarnold is correct, I just wanted to point out that the sizeof() call in your malloc() call in CreateList appears to have a bug. I believe it should be:
List *pList = malloc(sizeof(List));
The way you had it, you will create enough memory to hold a pointer to a List and not the List structure. (You may also want to cast the return type to (List*) and compare it to NULL before using it..)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.