test_objects.py 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. # This file is part of h5py, a Python interface to the HDF5 library.
  2. #
  3. # http://www.h5py.org
  4. #
  5. # Copyright 2008-2013 Andrew Collette and contributors
  6. #
  7. # License: Standard 3-clause BSD; see "license.txt" for full license terms
  8. # and contributor agreement.
  9. import os
  10. import threading
  11. import time
  12. import pytest
  13. from h5py import _objects as o
  14. from .common import TestCase
  15. class TestObjects(TestCase):
  16. def test_invalid(self):
  17. # Check for segfault on close
  18. oid = o.ObjectID(0)
  19. del oid
  20. oid = o.ObjectID(1)
  21. del oid
  22. def test_equality(self):
  23. # Identifier-based equality
  24. oid1 = o.ObjectID(42)
  25. oid2 = o.ObjectID(42)
  26. oid3 = o.ObjectID(43)
  27. self.assertEqual(oid1, oid2)
  28. self.assertNotEqual(oid1, oid3)
  29. def test_hash(self):
  30. # Default objects are not hashable
  31. oid = o.ObjectID(42)
  32. with self.assertRaises(TypeError):
  33. hash(oid)
  34. @pytest.mark.thread_unsafe(reason="fork() from a thread may deadlock")
  35. @pytest.mark.filterwarnings(
  36. # https://github.com/python/cpython/pull/100229
  37. r"ignore:.*use of fork\(\) may lead to deadlocks:DeprecationWarning"
  38. )
  39. @pytest.mark.skipif(not hasattr(os, "fork"), reason="fork() not available")
  40. def test_phil_fork_with_threads(self):
  41. """Test that handling of the phil Lock after fork is correct.
  42. h5py uses os.register_at_fork() to cause os.fork() to acquire the phil lock
  43. before forking and release it afterwards, so that the global state of libhdf5
  44. cannot be cloned in a corrupted state.
  45. """
  46. thread_acquired_phil_event = threading.Event()
  47. def f():
  48. # Simulate another thread holding the phil lock while it updates
  49. # the libhdf5 global state
  50. with o.phil:
  51. thread_acquired_phil_event.set()
  52. time.sleep(1)
  53. thread = threading.Thread(target=f)
  54. thread.start()
  55. thread_acquired_phil_event.wait()
  56. try:
  57. # Now fork the current (main) thread while the other thread holds the lock.
  58. # os.fork() acquires the phil lock, so this will block until the other
  59. # thread releases it.
  60. pid = os.fork()
  61. if pid == 0:
  62. # Child process
  63. # If we handle the phil lock correctly, this should not deadlock, and we
  64. # should be able to acquire the lock here.
  65. if o.phil.acquire(blocking=False):
  66. o.phil.release()
  67. os._exit(0)
  68. else:
  69. os._exit(1)
  70. else:
  71. # Parent process
  72. assert o.phil.acquire(blocking=False)
  73. o.phil.release()
  74. # Wait for the child process to finish
  75. _, status = os.waitpid(pid, 0)
  76. assert os.WIFEXITED(status)
  77. assert os.WEXITSTATUS(status) == 0
  78. finally:
  79. thread.join()