Comment on race for shared containers, added fourth type for container.

This commit is contained in:
Nikolay Igotti
2016-10-14 12:15:53 +03:00
parent 90a09ca356
commit 5bbb5a4d13
2 changed files with 22 additions and 2 deletions
+1 -2
View File
@@ -1,10 +1,9 @@
#include <stdlib.h>
#include <cassert>
#include "Memory.h"
void FreeObject(ContainerHeader* header) {
header->ref_count_ = CONTAINER_TAG_INVALID;
free(header);
}
+21
View File
@@ -18,6 +18,8 @@ typedef enum {
CONTAINER_TAG_NOCOUNT = 1,
// Container shall be atomically refcounted.
CONTAINER_TAG_SHARED = 2,
// Container is no longer valid.
CONTAINER_TAG_INVALID = 3,
// Actual value to increment/decrement conatiner by. Tag is in lower bits.
CONTAINER_TAG_INCREMENT = 1 << 2,
//
@@ -67,6 +69,9 @@ inline void AddRef(ContainerHeader* header) {
case CONTAINER_TAG_SHARED:
__sync_fetch_and_add(&header->ref_count_, CONTAINER_TAG_INCREMENT);
break;
case CONTAINER_TAG_INVALID:
RuntimeAssert(false, "trying to addref invalid container");
break;
}
}
@@ -81,12 +86,28 @@ inline void Release(ContainerHeader* header) {
break;
case CONTAINER_TAG_NOCOUNT:
break;
// Note that shared containers have potentially subtle race, if object holds a
// reference to another object, stored in shorter living container. In this
// case there's unlikely, but possible case, where one mutator takes reference,
// from the field, but not yet AddRef'ed it, while another mutator updates
// field with another value, and thus Release's same field.
// If those two updates happens concurrently - it may lead to dereference of stale
// pointer.
// It seems not a very big issue as:
// - if objects stored in the same container, race will never happen
// - if concurrent field access is under lock, race will never happen
// - container likely groups multiple objects, so object release will lead to
// container release only in relatively few cases, which will decrease race
// probability even further.
case CONTAINER_TAG_SHARED:
if (__sync_sub_and_fetch(
&header->ref_count_, CONTAINER_TAG_INCREMENT) == CONTAINER_TAG_SHARED) {
FreeObject(header);
}
break;
case CONTAINER_TAG_INVALID:
RuntimeAssert(false, "trying to release invalid container");
break;
}
}