Skip to content

Commit 7f6bc4b

Browse files
committed
Merge branch 'frozenset_immutable_tracking' of github.com:eendebakpt/cpython into frozenset_immutable_tracking
2 parents c05db54 + 2735a71 commit 7f6bc4b

4 files changed

Lines changed: 68 additions & 6 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import os
2+
import unittest
3+
4+
from test.support import import_helper, threading_helper
5+
from test.support.threading_helper import run_concurrently
6+
from uuid import SafeUUID
7+
8+
c_uuid = import_helper.import_module("_uuid")
9+
10+
NTHREADS = 10
11+
UUID_PER_THREAD = 1000
12+
13+
14+
@threading_helper.requires_working_threading()
15+
class UUIDTests(unittest.TestCase):
16+
@unittest.skipUnless(os.name == "posix", "POSIX only")
17+
def test_generate_time_safe(self):
18+
uuids = []
19+
20+
def worker():
21+
local_uuids = []
22+
for _ in range(UUID_PER_THREAD):
23+
uuid, is_safe = c_uuid.generate_time_safe()
24+
self.assertIs(type(uuid), bytes)
25+
self.assertEqual(len(uuid), 16)
26+
# Collect the UUID only if it is safe. If not, we cannot ensure
27+
# UUID uniqueness. According to uuid_generate_time_safe() man
28+
# page, it is theoretically possible for two concurrently
29+
# running processes to generate the same UUID(s) if the return
30+
# value is not 0.
31+
if is_safe == SafeUUID.safe:
32+
local_uuids.append(uuid)
33+
34+
# Merge all safe uuids
35+
uuids.extend(local_uuids)
36+
37+
run_concurrently(worker_func=worker, nthreads=NTHREADS)
38+
self.assertEqual(len(uuids), len(set(uuids)))
39+
40+
@unittest.skipUnless(os.name == "nt", "Windows only")
41+
def test_UuidCreate(self):
42+
uuids = []
43+
44+
def worker():
45+
local_uuids = []
46+
for _ in range(UUID_PER_THREAD):
47+
uuid = c_uuid.UuidCreate()
48+
self.assertIs(type(uuid), bytes)
49+
self.assertEqual(len(uuid), 16)
50+
local_uuids.append(uuid)
51+
52+
# Merge all uuids
53+
uuids.extend(local_uuids)
54+
55+
run_concurrently(worker_func=worker, nthreads=NTHREADS)
56+
self.assertEqual(len(uuids), len(set(uuids)))
57+
58+
59+
if __name__ == "__main__":
60+
unittest.main()

Lib/test/test_sys.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1876,7 +1876,10 @@ class S(set):
18761876
check(S(), set(), '3P')
18771877
class FS(frozenset):
18781878
__slots__ = 'a', 'b', 'c'
1879-
check(FS(), frozenset(), '3P')
1879+
1880+
class mytuple(tuple):
1881+
pass
1882+
check(FS([mytuple()]), frozenset([mytuple()]), '3P')
18801883
from collections import OrderedDict
18811884
class OD(OrderedDict):
18821885
__slots__ = 'a', 'b', 'c'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Frozenset objects with immutable elements are no longer tracked by the garbage collector.

Objects/setobject.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,10 +1177,10 @@ make_new_set_basetype(PyTypeObject *type, PyObject *iterable)
11771177
void
11781178
_PyFrozenSet_MaybeUntrack(PyObject *op)
11791179
{
1180-
if ((op ==NULL) || !(PyFrozenSet_CheckExact(op))) {
1180+
if ((op == NULL) || !(PyFrozenSet_CheckExact(op))) {
11811181
return;
11821182
}
1183-
// the frozenset is tracked by the GC. if all elements are immutable we can untrack
1183+
// if all elements of a frozenset are not tracked, we untrack the object
11841184
Py_ssize_t pos = 0;
11851185
setentry *entry;
11861186
while (set_next((PySetObject *)op, &pos, &entry)) {
@@ -2730,9 +2730,7 @@ PyObject *
27302730
PyFrozenSet_New(PyObject *iterable)
27312731
{
27322732
PyObject *result = make_new_set(&PyFrozenSet_Type, iterable);
2733-
if (result != NULL) {
2734-
_PyFrozenSet_MaybeUntrack(result);
2735-
}
2733+
_PyFrozenSet_MaybeUntrack(result);
27362734
return result;
27372735
}
27382736

0 commit comments

Comments
 (0)