Skip to content

py/map: Make map rehashing an atomic operation. #11604

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions py/map.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "py/mpconfig.h"
#include "py/misc.h"
#include "py/runtime.h"
#include "py/mphal.h"

#if MICROPY_DEBUG_VERBOSE // print debugging info
#define DEBUG_PRINT (1)
Expand Down Expand Up @@ -134,16 +135,28 @@ STATIC void mp_map_rehash(mp_map_t *map) {
DEBUG_printf("mp_map_rehash(%p): " UINT_FMT " -> " UINT_FMT "\n", map, old_alloc, new_alloc);
mp_map_elem_t *old_table = map->table;
mp_map_elem_t *new_table = m_new0(mp_map_elem_t, new_alloc);
// If we reach this point, table resizing succeeded, now we can edit the old map.
map->alloc = new_alloc;
map->used = 0;
map->all_keys_are_qstrs = 1;
map->table = new_table;

// If we reach this point then allocating the new table succeeded, so construct the new map.
mp_map_t new_map = {
.all_keys_are_qstrs = 1,
.is_fixed = 0,
.is_ordered = 0,
.used = 0,
.alloc = new_alloc,
.table = new_table,
};
for (size_t i = 0; i < old_alloc; i++) {
if (old_table[i].key != MP_OBJ_NULL && old_table[i].key != MP_OBJ_SENTINEL) {
mp_map_lookup(map, old_table[i].key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = old_table[i].value;
mp_map_lookup(&new_map, old_table[i].key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = old_table[i].value;
}
}

// Swap the old map for the new one.
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
*map = new_map;
MICROPY_END_ATOMIC_SECTION(atomic_state);

// Free the old table.
m_del(mp_map_elem_t, old_table, old_alloc);
}

Expand Down
79 changes: 79 additions & 0 deletions tests/thread/stress_globals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import time, _thread


def thread_task():
for _ in range(1000):
a00
print("pass")


print("start")
a00 = None

_thread.start_new_thread(thread_task, ())

# create lots of global variables
a01 = None
a02 = None
a03 = None
a04 = None
a05 = None
a06 = None
a07 = None
a10 = None
a11 = None
a12 = None
a13 = None
a14 = None
a15 = None
a16 = None
a17 = None
a18 = None
a19 = None
a20 = None
a21 = None
a22 = None
a23 = None
a24 = None
a25 = None
a26 = None
a27 = None
a28 = None
a29 = None
a30 = None
a31 = None
a32 = None
a33 = None
a34 = None
a35 = None
a36 = None
a37 = None
a38 = None
a39 = None
a40 = None
a41 = None
a42 = None
a43 = None
a44 = None
a45 = None
a46 = None
a47 = None
a48 = None
a49 = None
a50 = None
a51 = None
a52 = None
a53 = None
a54 = None
a55 = None
a56 = None
a57 = None
a58 = None
a59 = None
a60 = None
a61 = None
a62 = None
a63 = None

# let the thread finish
time.sleep(0.1)