fix race conditions in instance id-to-key lookup
this is just an intermediate step: firstly, iid-to-key lookup is a linear scan; secondly, the lookup should happen on the RHC or the WHC and not on some internal map to associates key values with instance handles and is independent of entities Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
		
							parent
							
								
									0f7ce1ac43
								
							
						
					
					
						commit
						179a35d192
					
				
					 3 changed files with 45 additions and 74 deletions
				
			
		| 
						 | 
				
			
			@ -250,9 +250,7 @@ dds_unregister_instance_ih_ts(
 | 
			
		|||
    bool autodispose = true;
 | 
			
		||||
    dds_write_action action = DDS_WR_ACTION_UNREGISTER;
 | 
			
		||||
    dds_entity *wr;
 | 
			
		||||
    struct ddsi_tkmap *map;
 | 
			
		||||
    const dds_topic *topic;
 | 
			
		||||
    void *sample;
 | 
			
		||||
    struct ddsi_tkmap_instance *tk;
 | 
			
		||||
 | 
			
		||||
    rc = dds_entity_lock(writer, DDS_KIND_WRITER, &wr);
 | 
			
		||||
    if (rc != DDS_RETCODE_OK) {
 | 
			
		||||
| 
						 | 
				
			
			@ -269,19 +267,21 @@ dds_unregister_instance_ih_ts(
 | 
			
		|||
        action |= DDS_WR_DISPOSE_BIT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    map = gv.m_tkmap;
 | 
			
		||||
    if (asleep) {
 | 
			
		||||
        thread_state_awake(thr);
 | 
			
		||||
    }
 | 
			
		||||
    topic = dds_instance_info((dds_entity*)wr);
 | 
			
		||||
    sample = ddsi_sertopic_alloc_sample (topic->m_stopic);
 | 
			
		||||
    if (ddsi_tkmap_get_key (map, topic->m_stopic, handle, sample)) {
 | 
			
		||||
    tk = ddsi_tkmap_find_by_id (gv.m_tkmap, handle);
 | 
			
		||||
    if (tk) {
 | 
			
		||||
        struct ddsi_sertopic *tp = ((dds_writer*) wr)->m_topic->m_stopic;
 | 
			
		||||
        void *sample = ddsi_sertopic_alloc_sample (tp);
 | 
			
		||||
        ddsi_serdata_topicless_to_sample (tp, tk->m_sample, sample, NULL, NULL);
 | 
			
		||||
        ddsi_tkmap_instance_unref (tk);
 | 
			
		||||
        ret = dds_write_impl ((dds_writer*)wr, sample, timestamp, action);
 | 
			
		||||
    } else{
 | 
			
		||||
        ddsi_sertopic_free_sample (tp, sample, DDS_FREE_ALL);
 | 
			
		||||
    } else {
 | 
			
		||||
        DDS_ERROR("No instance related with the provided handle is found\n");
 | 
			
		||||
        ret = DDS_ERRNO(DDS_RETCODE_PRECONDITION_NOT_MET);
 | 
			
		||||
    }
 | 
			
		||||
    ddsi_sertopic_free_sample (topic->m_stopic, sample, DDS_FREE_ALL);
 | 
			
		||||
    if (asleep) {
 | 
			
		||||
        thread_state_asleep(thr);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -387,14 +387,17 @@ dds_dispose_ih_ts(
 | 
			
		|||
    if (rc == DDS_RETCODE_OK) {
 | 
			
		||||
        struct thread_state1 * const thr = lookup_thread_state();
 | 
			
		||||
        const bool asleep = !vtime_awake_p(thr->vtime);
 | 
			
		||||
        struct ddsi_tkmap *map = gv.m_tkmap;
 | 
			
		||||
        const dds_topic *topic = dds_instance_info((dds_entity*)wr);
 | 
			
		||||
        void *sample = ddsi_sertopic_alloc_sample (topic->m_stopic);
 | 
			
		||||
        struct ddsi_tkmap_instance *tk;
 | 
			
		||||
        if (asleep) {
 | 
			
		||||
            thread_state_awake(thr);
 | 
			
		||||
        }
 | 
			
		||||
        if (ddsi_tkmap_get_key (map, topic->m_stopic, handle, sample)) {
 | 
			
		||||
            ret = dds_dispose_impl(wr, sample, handle, timestamp);
 | 
			
		||||
        if ((tk = ddsi_tkmap_find_by_id (gv.m_tkmap, handle)) != NULL) {
 | 
			
		||||
            struct ddsi_sertopic *tp = ((dds_writer*) wr)->m_topic->m_stopic;
 | 
			
		||||
            void *sample = ddsi_sertopic_alloc_sample (tp);
 | 
			
		||||
            ddsi_serdata_topicless_to_sample (tp, tk->m_sample, sample, NULL, NULL);
 | 
			
		||||
            ddsi_tkmap_instance_unref (tk);
 | 
			
		||||
            ret = dds_dispose_impl (wr, sample, handle, timestamp);
 | 
			
		||||
            ddsi_sertopic_free_sample (tp, sample, DDS_FREE_ALL);
 | 
			
		||||
        } else {
 | 
			
		||||
            DDS_ERROR("No instance related with the provided handle is found\n");
 | 
			
		||||
            ret = DDS_ERRNO(DDS_RETCODE_PRECONDITION_NOT_MET);
 | 
			
		||||
| 
						 | 
				
			
			@ -402,7 +405,6 @@ dds_dispose_ih_ts(
 | 
			
		|||
        if (asleep) {
 | 
			
		||||
            thread_state_asleep(thr);
 | 
			
		||||
        }
 | 
			
		||||
        ddsi_sertopic_free_sample (topic->m_stopic, sample, DDS_FREE_ALL);
 | 
			
		||||
        dds_writer_unlock(wr);
 | 
			
		||||
    } else {
 | 
			
		||||
        DDS_ERROR("Error occurred on locking writer\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -461,14 +463,14 @@ _Pre_satisfies_(entity & DDS_ENTITY_KIND_MASK)
 | 
			
		|||
dds_return_t
 | 
			
		||||
dds_instance_get_key(
 | 
			
		||||
        dds_entity_t entity,
 | 
			
		||||
        dds_instance_handle_t inst,
 | 
			
		||||
        dds_instance_handle_t ih,
 | 
			
		||||
        void *data)
 | 
			
		||||
{
 | 
			
		||||
    struct thread_state1 * const thr = lookup_thread_state();
 | 
			
		||||
    const bool asleep = !vtime_awake_p(thr->vtime);
 | 
			
		||||
    dds_return_t ret;
 | 
			
		||||
    const dds_topic * topic;
 | 
			
		||||
    struct ddsi_tkmap * map = gv.m_tkmap;
 | 
			
		||||
    struct ddsi_tkmap_instance * tk;
 | 
			
		||||
 | 
			
		||||
    if(data == NULL){
 | 
			
		||||
        DDS_ERROR("Argument data is NULL\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -482,13 +484,15 @@ dds_instance_get_key(
 | 
			
		|||
        ret = DDS_ERRNO(DDS_RETCODE_BAD_PARAMETER);
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
    ddsi_sertopic_zero_sample (topic->m_stopic, data);
 | 
			
		||||
    if (asleep) {
 | 
			
		||||
        thread_state_awake(thr);
 | 
			
		||||
    }
 | 
			
		||||
    if (ddsi_tkmap_get_key (map, topic->m_stopic, inst, data)) {
 | 
			
		||||
    if ((tk = ddsi_tkmap_find_by_id(gv.m_tkmap, ih)) != NULL) {
 | 
			
		||||
        ddsi_sertopic_zero_sample (topic->m_stopic, data);
 | 
			
		||||
        ddsi_serdata_topicless_to_sample (topic->m_stopic, tk->m_sample, data, NULL, NULL);
 | 
			
		||||
        ddsi_tkmap_instance_unref (tk);
 | 
			
		||||
        ret = DDS_RETCODE_OK;
 | 
			
		||||
    } else{
 | 
			
		||||
    } else {
 | 
			
		||||
        DDS_ERROR("No instance related with the provided entity is found\n");
 | 
			
		||||
        ret = DDS_ERRNO(DDS_RETCODE_BAD_PARAMETER);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,7 +36,6 @@ struct ddsi_tkmap * ddsi_tkmap_new (void);
 | 
			
		|||
void ddsi_tkmap_free (_Inout_ _Post_invalid_ struct ddsi_tkmap *tkmap);
 | 
			
		||||
void ddsi_tkmap_instance_ref (_In_ struct ddsi_tkmap_instance *tk);
 | 
			
		||||
uint64_t ddsi_tkmap_lookup (_In_ struct ddsi_tkmap *tkmap, _In_ const struct ddsi_serdata *serdata);
 | 
			
		||||
_Check_return_ bool ddsi_tkmap_get_key (_In_ struct ddsi_tkmap * map, const struct ddsi_sertopic *topic, _In_ uint64_t iid, _Out_ void * sample);
 | 
			
		||||
_Check_return_ struct ddsi_tkmap_instance * ddsi_tkmap_find(
 | 
			
		||||
        _In_ struct ddsi_serdata * sd,
 | 
			
		||||
        _In_ const bool rd,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,7 +82,7 @@ static int dds_tk_equals_void (const void *a, const void *b)
 | 
			
		|||
  return dds_tk_equals (a, b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ddsi_tkmap * ddsi_tkmap_new (void)
 | 
			
		||||
struct ddsi_tkmap *ddsi_tkmap_new (void)
 | 
			
		||||
{
 | 
			
		||||
  struct ddsi_tkmap *tkmap = dds_alloc (sizeof (*tkmap));
 | 
			
		||||
  tkmap->m_hh = ut_chhNew (1, dds_tk_hash_void, dds_tk_equals_void, gc_buckets);
 | 
			
		||||
| 
						 | 
				
			
			@ -111,64 +111,32 @@ uint64_t ddsi_tkmap_lookup (_In_ struct ddsi_tkmap * map, _In_ const struct ddsi
 | 
			
		|||
{
 | 
			
		||||
  struct ddsi_tkmap_instance dummy;
 | 
			
		||||
  struct ddsi_tkmap_instance * tk;
 | 
			
		||||
  assert (vtime_awake_p(lookup_thread_state()->vtime));
 | 
			
		||||
  dummy.m_sample = (struct ddsi_serdata *) sd;
 | 
			
		||||
  tk = ut_chhLookup (map->m_hh, &dummy);
 | 
			
		||||
  return (tk) ? tk->m_iid : DDS_HANDLE_NIL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  const struct ddsi_sertopic *topic;
 | 
			
		||||
  uint64_t m_iid;
 | 
			
		||||
  void * m_sample;
 | 
			
		||||
  bool m_ret;
 | 
			
		||||
}
 | 
			
		||||
tkmap_get_key_arg;
 | 
			
		||||
 | 
			
		||||
static void dds_tkmap_get_key_fn (void * vtk, void * varg)
 | 
			
		||||
{
 | 
			
		||||
  struct ddsi_tkmap_instance * tk = vtk;
 | 
			
		||||
  tkmap_get_key_arg * arg = (tkmap_get_key_arg*) varg;
 | 
			
		||||
  if (tk->m_iid == arg->m_iid)
 | 
			
		||||
  {
 | 
			
		||||
    ddsi_serdata_topicless_to_sample (arg->topic, tk->m_sample, arg->m_sample, 0, 0);
 | 
			
		||||
    arg->m_ret = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_Check_return_
 | 
			
		||||
bool ddsi_tkmap_get_key (_In_ struct ddsi_tkmap * map, const struct ddsi_sertopic *topic, _In_ uint64_t iid, _Out_ void * sample)
 | 
			
		||||
struct ddsi_tkmap_instance *ddsi_tkmap_find_by_id (_In_ struct ddsi_tkmap *map, _In_ uint64_t iid)
 | 
			
		||||
{
 | 
			
		||||
  tkmap_get_key_arg arg = { topic, iid, sample, false };
 | 
			
		||||
  os_mutexLock (&map->m_lock);
 | 
			
		||||
  ut_chhEnumUnsafe (map->m_hh, dds_tkmap_get_key_fn, &arg);
 | 
			
		||||
  os_mutexUnlock (&map->m_lock);
 | 
			
		||||
  return arg.m_ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  uint64_t m_iid;
 | 
			
		||||
  struct ddsi_tkmap_instance * m_inst;
 | 
			
		||||
}
 | 
			
		||||
tkmap_get_inst_arg;
 | 
			
		||||
 | 
			
		||||
static void dds_tkmap_get_inst_fn (void * vtk, void * varg)
 | 
			
		||||
{
 | 
			
		||||
  struct ddsi_tkmap_instance * tk = vtk;
 | 
			
		||||
  tkmap_get_inst_arg * arg = (tkmap_get_inst_arg*) varg;
 | 
			
		||||
  if (tk->m_iid == arg->m_iid)
 | 
			
		||||
  {
 | 
			
		||||
    arg->m_inst = tk;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_Check_return_
 | 
			
		||||
struct ddsi_tkmap_instance * ddsi_tkmap_find_by_id (_In_ struct ddsi_tkmap * map, _In_ uint64_t iid)
 | 
			
		||||
{
 | 
			
		||||
  tkmap_get_inst_arg arg = { iid, NULL };
 | 
			
		||||
  ut_chhEnumUnsafe (map->m_hh, dds_tkmap_get_inst_fn, &arg);
 | 
			
		||||
  return arg.m_inst;
 | 
			
		||||
  /* This is not a function that should be used liberally, as it linearly scans the key-to-iid map. */
 | 
			
		||||
  struct ut_chhIter it;
 | 
			
		||||
  struct ddsi_tkmap_instance *tk;
 | 
			
		||||
  uint32_t refc;
 | 
			
		||||
  assert (vtime_awake_p(lookup_thread_state()->vtime));
 | 
			
		||||
  for (tk = ut_chhIterFirst (map->m_hh, &it); tk; tk = ut_chhIterNext (&it))
 | 
			
		||||
    if (tk->m_iid == iid)
 | 
			
		||||
      break;
 | 
			
		||||
  if (tk == NULL)
 | 
			
		||||
    /* Common case of it not existing at all */
 | 
			
		||||
    return NULL;
 | 
			
		||||
  else if (!((refc = os_atomic_ld32 (&tk->m_refc)) & REFC_DELETE) && os_atomic_cas32 (&tk->m_refc, refc, refc+1))
 | 
			
		||||
    /* Common case of it existing, not in the process of being deleted and no one else concurrently modifying the refcount */
 | 
			
		||||
    return tk;
 | 
			
		||||
  else
 | 
			
		||||
    /* Let key value lookup handle the possible CAS loop and the complicated cases */
 | 
			
		||||
    return ddsi_tkmap_find (tk->m_sample, false, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Debug keyhash generation for debug and coverage builds */
 | 
			
		||||
| 
						 | 
				
			
			@ -193,6 +161,7 @@ struct ddsi_tkmap_instance * ddsi_tkmap_find(
 | 
			
		|||
  struct ddsi_tkmap_instance * tk;
 | 
			
		||||
  struct ddsi_tkmap * map = gv.m_tkmap;
 | 
			
		||||
 | 
			
		||||
  assert (vtime_awake_p(lookup_thread_state()->vtime));
 | 
			
		||||
  dummy.m_sample = sd;
 | 
			
		||||
retry:
 | 
			
		||||
  if ((tk = ut_chhLookup(map->m_hh, &dummy)) != NULL)
 | 
			
		||||
| 
						 | 
				
			
			@ -242,7 +211,6 @@ retry:
 | 
			
		|||
_Check_return_
 | 
			
		||||
struct ddsi_tkmap_instance * ddsi_tkmap_lookup_instance_ref (_In_ struct ddsi_serdata * sd)
 | 
			
		||||
{
 | 
			
		||||
  assert (vtime_awake_p (lookup_thread_state ()->vtime));
 | 
			
		||||
  return ddsi_tkmap_find (sd, true, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue