# Is it safe to use a global hash table in an ocaml-5 parallel program?

**URL:** <https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144>\
**Category:** Learning\
**Tags:** multicore-ocaml\
**Created:** [January 10, 2023, 2:07am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144 "2023-01-10T02:07:32Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![UnixJunkie](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unixjunkie/32/638_2.png) [@UnixJunkie](https://discuss.ocaml.org/u/UnixJunkie)\
**Post date:** [January 10, 2023, 2:07am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/1 "2023-01-10T02:07:32Z")

</div>

Especially when following the constraint: different threads (“domains” in ocaml-5 parlance) never use the same keys?  
I.e. each domain uses a set of keys which is not overlapping with any other domain’s set of keys.

I later realised (after asking this question) that the stdlib’s documentation clearly says no (the documentation was updated because of ocaml-5):  
“Unsynchronized accesses to a hash table may lead to an invalid hash table state. Thus, concurrent accesses to a hash tables must be synchronized (for instance with a [`Mutex.t`](https://v2.ocaml.org/api/Mutex.html#TYPEt)).”

---

<div class="post-metadata">

**Author:** ![silene](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/silene/32/2707_2.png) [@silene](https://discuss.ocaml.org/u/silene)\
**Post date:** [January 10, 2023, 4:37am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/2 "2023-01-10T04:37:57Z")

</div>

It is memory-safe, but it is not thread-safe, as there is no synchronization around hashtable accesses. For example, the resizing of an overgrown hashtable will happen concurrently to any other access, which means that elements might get lost, stored in the wrong buckets, etc.

---

<div class="post-metadata">

**Author:** ![UnixJunkie](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unixjunkie/32/638_2.png) [@UnixJunkie](https://discuss.ocaml.org/u/UnixJunkie)\
**Post date:** [January 10, 2023, 4:55am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/3 "2023-01-10T04:55:12Z")

</div>

So, operations modifying the hash table should use a semaphore?

Or, even read operations should use a semaphore?

What if the hash table is created big enough and will never need to be resized at runtime?

---

<div class="post-metadata">

**Author:** ![gasche](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gasche/32/4_2.png) [@gasche](https://discuss.ocaml.org/u/gasche)\
**Post date:** [January 10, 2023, 5:46am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/4 "2023-01-10T05:46:37Z")

</div>

The documentation says that unsynchronized access to a hashtable are a programming error. So you shouldn’t do that; either protect the table with a lock, or use a different implementation of a “concurrent” hashtable. (An atomic reference on a persistent map also works fine.)

Going in the details as you asked: in absence of resizing, a shared hashtable is safe. In presence of resizing, additions/removal may race with any other operation (including reads), so both mutations and reads need synchronization. See [[multicore] does Hashtbl respect separation? · Issue #11681 · ocaml/ocaml · GitHub](https://github.com/ocaml/ocaml/issues/11681) where I was asking basically the same question.

---

<div class="post-metadata">

**Author:** ![silene](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/silene/32/2707_2.png) [@silene](https://discuss.ocaml.org/u/silene)\
**Post date:** [January 10, 2023, 6:11am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/5 "2023-01-10T06:11:31Z")

</div>

> [@gasche](#):
>
> in absence of resizing, a shared hashtable is safe.

It is not. Consider the following trace:

```ocaml
(* replace *) (* iter *)
| Cons ({key=k; next} as slot) ->
  if H.equal k key
  then (
    slot.key <- key;
                                    | Cons{key; data; next} ->
                                      f key data;
    slot.data <- data;
    false)

```

If `H.equal` is not a strict equality but some larger equivalence relation, then `Hashtbl.iter` will pass the wrong data associated to a key.

---

<div class="post-metadata">

**Author:** ![UnixJunkie](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unixjunkie/32/638_2.png) [@UnixJunkie](https://discuss.ocaml.org/u/UnixJunkie)\
**Post date:** [January 10, 2023, 6:44am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/6 "2023-01-10T06:44:24Z")

</div>

I was starting to think about that: an atomic reference to a persistent map.  
It could give me what I need and better guarantees.

---

<div class="post-metadata">

**Author:** ![bluddy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/bluddy/32/104_2.png) [@bluddy](https://discuss.ocaml.org/u/bluddy)\
**Post date:** [January 10, 2023, 6:50am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/7 "2023-01-10T06:50:21Z")

</div>

Notice that we’re talking about different threads using different keys i.e. sharding the hashtable across domains.

---

<div class="post-metadata">

**Author:** ![bluddy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/bluddy/32/104_2.png) [@bluddy](https://discuss.ocaml.org/u/bluddy)\
**Post date:** [January 10, 2023, 7:10am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/8 "2023-01-10T07:10:27Z")

</div>

If each domain only uses its own keys, you’re likely better off just creating an array with a hashtable per domain.

---

<div class="post-metadata">

**Author:** ![silene](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/silene/32/2707_2.png) [@silene](https://discuss.ocaml.org/u/silene)\
**Post date:** [January 10, 2023, 7:34am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/9 "2023-01-10T07:34:07Z")

</div>

> [@bluddy](#):
>
> we’re talking about different threads using different keys i.e. sharding the hashtable across domains.

That does not matter. Consider removal for instance (which never resizes the hashtable). If you remove concurrently two different keys from the same bucket, you will end up with a bucket that still contains one of the keys if they were consecutive in the bucket.

The idea of “separation” might seem nice. But unfortunately, the safety condition is not that the threads never access equivalent keys, it is more like they never access the same buckets.

---

<div class="post-metadata">

**Author:** ![bluddy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/bluddy/32/104_2.png) [@bluddy](https://discuss.ocaml.org/u/bluddy)\
**Post date:** [January 10, 2023, 7:46am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/10 "2023-01-10T07:46:48Z")

</div>

That’s a good point.

Either way, if the domains aren’t communicating anyway, just have a hashtable per domain

---

<div class="post-metadata">

**Author:** ![UnixJunkie](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unixjunkie/32/638_2.png) [@UnixJunkie](https://discuss.ocaml.org/u/UnixJunkie)\
**Post date:** [January 10, 2023, 7:52am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/11 "2023-01-10T07:52:33Z")

</div>

Hence, I wonder why the documentation of Domain.DLS.new\_key is so complex and the interface so restrictive.

---

<div class="post-metadata">

**Author:** ![gasche](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gasche/32/4_2.png) [@gasche](https://discuss.ocaml.org/u/gasche)\
**Post date:** [January 10, 2023, 3:45pm UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/12 "2023-01-10T15:45:37Z")

</div>

> [@UnixJunkie](#):
>
> I wonder why the documentation of Domain.DLS.new\_key is so complex and the interface so restrictive.

If you have specific criticisms or needs that you can justify, please go ahead. As such, your feedback is not actionable. Maybe you should explain what you need to do and why you have trouble doing it.

---

<div class="post-metadata">

**Author:** ![UnixJunkie](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unixjunkie/32/638_2.png) [@UnixJunkie](https://discuss.ocaml.org/u/UnixJunkie)\
**Post date:** [January 11, 2023, 1:14am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/13 "2023-01-11T01:14:08Z")

</div>

The expected type of a hash table is: 'a key to 'b value. Not 'a key to 'a.

---

<div class="post-metadata">

**Author:** ![sudha](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/sudha/32/3963_2.png) [@sudha](https://discuss.ocaml.org/u/sudha)\
**Post date:** [January 11, 2023, 5:44am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/14 "2023-01-11T05:44:59Z")

</div>

`Domain.DLS` is not a hash table. It lets you store values local to a domain and access them at a later point via unique keys bound to specific values.

---

<div class="post-metadata">

**Author:** ![UnixJunkie](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unixjunkie/32/638_2.png) [@UnixJunkie](https://discuss.ocaml.org/u/UnixJunkie)\
**Post date:** [January 11, 2023, 8:42am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/15 "2023-01-11T08:42:34Z")

</div>

This is kind of my critique: it could/should be a hash table local/private to a domain, then it would be much more useful and the documentation could be simpler.

---

<div class="post-metadata">

**Author:** ![bluddy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/bluddy/32/104_2.png) [@bluddy](https://discuss.ocaml.org/u/bluddy)\
**Post date:** [January 11, 2023, 9:07am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/16 "2023-01-11T09:07:50Z")

</div>

I don’t know that DLS is a good general-purpose solution. Also, the reason it’s complex is that the DLS needs to serve as a heterogenous map solution, which makes the typing situation complicated.

---

<div class="post-metadata">

**Author:** ![gasche](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gasche/32/4_2.png) [@gasche](https://discuss.ocaml.org/u/gasche)\
**Post date:** [January 11, 2023, 9:42am UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/17 "2023-01-11T09:42:10Z")

</div>

> [@UnixJunkie](#):
>
> This is kind of my critique: it could/should be a hash table local/private to a domain, then it would be much more useful and the documentation could be simpler.

This does not make any sense to me; we use DLS keys to store one value per domain, not to store several values on a single domain. (Of course a DLS value could itself be a hashtable.) So your proposal sounds like it would not fill the need that DLS was designed for. Again, maybe you should explain what you need and why you need it. (And maybe it should be your own library and not the DLS module, if it is doing something different.)

One aspect of DLS that is known but not accurately described in the documentation yet is that the implementation is not designed to gracefully handle “lots of keys”. Datastructure implementations that would try to create a new DLS key per instance of the datastructure should use something different. The DLS module was designed for “singleton state” in modules/libraries, that should now have one state variant per domain.

---

<div class="post-metadata">

**Author:** ![BikalGurung](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/bikalgurung/32/5105_2.png) [@BikalGurung](https://discuss.ocaml.org/u/BikalGurung)\
**Post date:** [January 11, 2023, 1:24pm UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/18 "2023-01-11T13:24:36Z")

</div>

This is interesting, how about in use-cases where we have a Module global hashtable which is loaded (key, value added) during module load time and thereafter only the find operations are performed. Is this still safe without the mutex and such? Or even in this use case do they need to be protected with mutex?  
i.e.

```ocaml
module M = struct
  let h = Hashtbl.create 5
  let () = 
    Hashtbl.replace ht "a" "val a" ;
    Hashtbl.replace ht "b" "val b" ;

 let doa () : string = 
  match Hashtbl.find_opt ht "a" with
  | Some v -> v
  | None -> ""

  let dob () ....
  let doc () ...
end 

```

Here `doa`, `dob` and `doc` only does `find` operation on `ht`. Is this access/usage pattern safe without mutex, et al?

---

<div class="post-metadata">

**Author:** ![gasche](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gasche/32/4_2.png) [@gasche](https://discuss.ocaml.org/u/gasche)\
**Post date:** [January 11, 2023, 1:56pm UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/19 "2023-01-11T13:56:11Z")

</div>

I _think_ that, with the current implementation, this is actually safe. This being said, it could not be (for example a splay tree would not satisfy this expectation). Why is everyone trying to ignore what the documentation says and come as close as possible to making their program buggy?

For this use-case I would just use a `'a Map.Make(String).t Atomic.t`. It may be slightly slower, but you sleep soundly at night, you are not relying on fragile internal details of a datastructure that explicitly documents again what you are doing.

---

<div class="post-metadata">

**Author:** ![gadmm](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gadmm/32/877_2.png) [@gadmm](https://discuss.ocaml.org/u/gadmm)\
**Post date:** [January 11, 2023, 2:49pm UTC](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144/20 "2023-01-11T14:49:10Z")

</div>

Because creating a data structure by mutation and then publishing it to the rest of the program in read-only fashion is a very common idiom.

This is why documenting “thread-unsafe” is insufficient. You lack for instance the distinction between what mutates and does not mutate data.

[Next page](https://discuss.ocaml.org/t/is-it-safe-to-use-a-global-hash-table-in-an-ocaml-5-parallel-program/11144.md?page=2)
