uuid.uuid1, uuid_generate_time и потоки

В Python я могу последовательно генерировать ошибку сегмента в uuid модуль. Это можно сделать, позвонив uuid.uuid1() неоднократно из нескольких потоков. После некоторого копания кажется, что эта функция в конце концов вызывает C uuid_generate_time функция через ctypes:

Из uuid.py:

for libname in ['uuid', 'c']:
    try:
        lib = ctypes.CDLL(ctypes.util.find_library(libname))
    except:
        continue
    if hasattr(lib, 'uuid_generate_random'):
        _uuid_generate_random = lib.uuid_generate_random
    if hasattr(lib, 'uuid_generate_time'):
        _uuid_generate_time = lib.uuid_generate_time
        if _uuid_generate_random is not None:
            break  # found everything we were looking for

И позже в определении uuid1():

def uuid1(node=None, clock_seq=None):
    """Generate a UUID from a host ID, sequence number, and the current time.
    If 'node' is not given, getnode() is used to obtain the hardware
    address.  If 'clock_seq' is given, it is used as the sequence number;
    otherwise a random 14-bit sequence number is chosen."""

    # When the system provides a version-1 UUID generator, use it (but don't
    # use UuidCreate here because its UUIDs don't conform to RFC 4122).
    if _uuid_generate_time and node is clock_seq is None:
        _buffer = ctypes.create_string_buffer(16)
        _uuid_generate_time(_buffer)
        return UUID(bytes=_buffer.raw)

Я прочитал страницы руководства для uuid_generate_time а также документы по Python для uuid.uuid1 и нет никакого упоминания о безопасности потока. Я предполагаю, что это как-то связано с тем, что ему нужен доступ к системным часам и / или MAC-адресам, но это только слепое предположение.

Мне было интересно, может ли кто-нибудь просветить меня?

Ниже приведен код, который я использовал для генерации ошибки сегмента:

import threading, uuid

# XXX If I use a lock, I can avoid the seg fault
#uuid_lock = threading.Lock()
def test_uuid(test_func):
  for i in xrange(100):
    test_func()
    #with uuid_lock:
    #  test_func()

def test(test_func, threads):
  print 'Running %s with %s threads...' % (test_func.__name__, threads)

  workers = [threading.Thread(target=test_uuid, args=(test_func,)) for x in xrange(threads)]
  [x.start() for x in workers]
  [x.join() for x in workers]
  print 'Done!'

if __name__ == '__main__':
  test(uuid.uuid4, 8)
  test(uuid.uuid1, 8)

Вывод, который я получаю:

Running uuid4 with 8 threads...
Done!
Running uuid1 with 8 threads...
Segmentation Fault (core dumped)

О, и я запускаю это на Солярисе...

1 ответ

В документации не говорится, что это потокобезопасно, поэтому вы не можете предположить, что это так. Это так просто.

Глядя на текущий источник OpenIndiana для uuid_generate_time не совсем очевидно, что является причиной ошибки. Однако, хотя функция использует блокировку, она не удерживает эту блокировку при выполнении ряда задач инициализации. Вероятно, это связано с проблемой, но я не могу указать конкретное место, где состояние гонки может вызвать ошибку. Вы можете попробовать позвонить uuid1 один раз, прежде чем запускать какие-либо темы и посмотреть, если это решит проблему. Хотя вам лучше использовать собственную блокировку, потому что нет никакой гарантии, что сам код Python является поточно-ориентированным.

Другие вопросы по тегам