Repository: pydicom/pynetdicom

Issue:
Exception in State Machine for AE-6 while in Sta2
**Steps To Reproduce**
Run `storescp 11112 -d` and in another terminal run `echoscu localhost 11112 -d -ta 0.001`

```
D: storescp.py v0.6.0
D: 
D: Request Parameters:
D: ======================= INCOMING A-ASSOCIATE-RQ PDU ========================
D: Their Implementation Class UID:      1.2.826.0.1.3680043.9.3811.1.5.7
D: Their Implementation Version Name:   PYNETDICOM_157
D: Application Context Name:    1.2.840.10008.3.1.1.1
D: Calling Application Name:    ECHOSCU         
D: Called Application Name:     ANY-SCP         
D: Their Max PDU Receive Size:  16382
D: Presentation Context:
D:   Context ID:        1 (Proposed)
D:     Abstract Syntax: =Verification SOP Class
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntaxes:
D:       =Explicit VR Little Endian
D:       =Implicit VR Little Endian
D:       =Deflated Explicit VR Little Endian
D:       =Explicit VR Big Endian
D: Requested Extended Negotiation: None
D: Requested Common Extended Negotiation: None
D: Requested Asynchronous Operations Window Negotiation: None
D: Requested User Identity Negotiation: None
D: ========================== END A-ASSOCIATE-RQ PDU ==========================
D: Abort Parameters:
D: =========================== INCOMING A-ABORT PDU ===========================
D: Abort Source: DUL service-user
D: Abort Reason: No reason given
D: ============================= END A-ABORT PDU ==============================
E: State Machine received an exception attempting to perform the action 'AE-6' while in state 'Sta2'
E: 'A_ABORT_RQ' object has no attribute 'protocol_version'
Traceback (most recent call last):
  File ".../pynetdicom/fsm.py", line 76, in do_action
    next_state = action[1](self.dul)
  File ".../pynetdicom/fsm.py", line 296, in AE_6
    if dul.pdu.protocol_version != 0x0001:
AttributeError: 'A_ABORT_RQ' object has no attribute 'protocol_version'
```
Looks like its been occurring at least since v1.5.0.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/pydicom/pynetdicom/pull/722?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) Report
> Merging [#722](https://codecov.io/gh/pydicom/pynetdicom/pull/722?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) (4c54019) into [master](https://codecov.io/gh/pydicom/pynetdicom/commit/b627c27a9dc370a53e1b495efef4bbdbfbfa29fd?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) (b627c27) will **decrease** coverage by `0.15%`.
> The diff coverage is `100.00%`.

```diff
@@             Coverage Diff             @@
##            master     #722      +/-   ##
===========================================
- Coverage   100.00%   99.84%   -0.16%     
===========================================
  Files           28       28              
  Lines         8622     8652      +30     
===========================================
+ Hits          8622     8639      +17     
- Misses           0       13      +13     
```

| [Impacted Files](https://codecov.io/gh/pydicom/pynetdicom/pull/722?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) | Coverage Δ | |
|---|---|---|
| [pynetdicom/dimse.py](https://codecov.io/gh/pydicom/pynetdicom/pull/722/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS9kaW1zZS5weQ==) | `100.00% <ø> (ø)` | |
| [pynetdicom/acse.py](https://codecov.io/gh/pydicom/pynetdicom/pull/722/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS9hY3NlLnB5) | `100.00% <100.00%> (ø)` | |
| [pynetdicom/dul.py](https://codecov.io/gh/pydicom/pynetdicom/pull/722/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS9kdWwucHk=) | `100.00% <100.00%> (ø)` | |
| [pynetdicom/fsm.py](https://codecov.io/gh/pydicom/pynetdicom/pull/722/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS9mc20ucHk=) | `100.00% <100.00%> (ø)` | |
| [pynetdicom/utils.py](https://codecov.io/gh/pydicom/pynetdicom/pull/722/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS91dGlscy5weQ==) | `88.79% <0.00%> (-11.21%)` | :arrow_down: |


Patch:
diff --git a/docs/changelog/v2.0.0.rst b/docs/changelog/v2.0.0.rst
index 838c0943c..3987a740a 100644
--- a/docs/changelog/v2.0.0.rst
+++ b/docs/changelog/v2.0.0.rst
@@ -35,6 +35,9 @@ Fixes
 * Fixed `qrscp --clean` (:issue:`681`, :issue:`682`)
 * Fixed a memory leak when running :class:`~pynetdicom.transport.AssociationServer`
   due to old Association threads not being garbage collected (:issue:`669`)
+* Fixed an exception in the state machine during association negotiation as the acceptor
+  when an A-ABORT-RQ PDU is received before the initial A-ASSOCIATE-RQ has been
+  processed (:issue:`721`)
 
 
 Enhancements
@@ -95,3 +98,6 @@ Changes
   ASCII :class:`str` rather than :class:`bytes`
 * The default bind address used when making association requests changed to
   ``("127.0.0.1", 0)`` (:issue:`680`)
+* ``DULServiceProvider.primitive`` and ``DULServiceProvider.pdu`` have been removed
+* :meth:`AssociationSocket.connect<pynetdicom.transport.AssociationSocket.connect>`
+  now takes a :class:`~pynetdicom.transport.T_CONNECT` primitive
diff --git a/docs/reference/transport.rst b/docs/reference/transport.rst
index 3b08b2c65..11ae9f8d4 100644
--- a/docs/reference/transport.rst
+++ b/docs/reference/transport.rst
@@ -19,3 +19,4 @@ communication with peer AEs.
    AssociationServer
    RequestHandler
    ThreadedAssociationServer
+   T_CONNECT
diff --git a/pynetdicom/acse.py b/pynetdicom/acse.py
index fa7698ab3..271855b3a 100644
--- a/pynetdicom/acse.py
+++ b/pynetdicom/acse.py
@@ -253,11 +253,6 @@ class ACSE:
         """Return the :class:`~pynetdicom.dul.DULServiceProvider`."""
         return self.assoc.dul
 
-    @property
-    def socket(self) -> Optional["AssociationSocket"]:
-        """Return the :class:`~pynetdicom.transport.AssociationSocket`."""
-        return self.assoc.dul.socket
-
     def is_aborted(self, abort_type: str = "both") -> bool:
         """Return ``True`` if an A-ABORT and/or A-P-ABORT request has been
         received.
@@ -870,3 +865,8 @@ class ACSE:
 
         # Send the A-ASSOCIATE request primitive to the peer
         self.dul.send_pdu(primitive)
+
+    @property
+    def socket(self) -> Optional["AssociationSocket"]:
+        """Return the :class:`~pynetdicom.transport.AssociationSocket`."""
+        return self.assoc.dul.socket
diff --git a/pynetdicom/dimse.py b/pynetdicom/dimse.py
index c7ace5078..caf8a8a38 100644
--- a/pynetdicom/dimse.py
+++ b/pynetdicom/dimse.py
@@ -298,6 +298,8 @@ class DIMSEServiceProvider:
                 LOGGER.error("Received an invalid DIMSE message")
                 LOGGER.exception(exc)
                 self.dul.event_queue.put("Evt19")
+                # Shouldn't need to reset `self.message` as Evt19 will end
+                #   the association
                 return
 
             # Keep C-CANCEL requests separate from other messages
diff --git a/pynetdicom/dul.py b/pynetdicom/dul.py
index 36538c10a..df493181b 100644
--- a/pynetdicom/dul.py
+++ b/pynetdicom/dul.py
@@ -8,7 +8,7 @@ import socket
 import struct
 from threading import Thread
 import time
-from typing import TYPE_CHECKING, Optional, Tuple, cast, Dict, Type
+from typing import TYPE_CHECKING, Optional, Tuple, cast, Dict, Type, Union
 
 from pynetdicom import evt
 from pynetdicom.fsm import StateMachine
@@ -31,12 +31,15 @@ from pynetdicom.pdu_primitives import (
     _PDUPrimitiveType,
 )
 from pynetdicom.timer import Timer
+from pynetdicom.transport import T_CONNECT
 from pynetdicom.utils import make_target
 
 if TYPE_CHECKING:  # pragma: no cover
     from pynetdicom.association import Association
     from pynetdicom.transport import AssociationSocket
 
+    _QueueType = queue.Queue[Union[_PDUPrimitiveType, T_CONNECT]]
+
 
 LOGGER = logging.getLogger("pynetdicom.dul")
 
@@ -53,11 +56,9 @@ class DULServiceProvider(Thread):
         <https://docs.python.org/3/library/socket.html#socket-objects>`_
         object used to communicate with the peer.
     to_provider_queue : queue.Queue
-        Queue of PDUs from the DUL service user to be processed by the DUL
-        provider.
+        Queue of primitives received from the peer to be processed by the service user.
     to_user_queue : queue.Queue
-        Queue of primitives from the DUL service to be processed by the DUL
-        user.
+        Queue of processed PDUs for the DUL service user.
     event_queue : queue.Queue
         List of queued events to be processed by the state machine.
     state_machine : fsm.StateMachine
@@ -77,22 +78,20 @@ class DULServiceProvider(Thread):
         self._assoc = assoc
         self.socket: Optional["AssociationSocket"] = None
 
-        # Current primitive and PDU
-        # TODO: Don't do it this way
-        self.primitive: Optional[_PDUPrimitiveType] = None
-        self.pdu: Optional[_PDUType] = None
-
         # Tracks the events the state machine needs to process
         self.event_queue: "queue.Queue[str]" = queue.Queue()
         # These queues provide communication between the DUL service
         #   user and the DUL service provider.
         # An event occurs when the DUL service user adds to
         #   the to_provider_queue
-        self.to_provider_queue: "queue.Queue[_PDUPrimitiveType]" = queue.Queue()
+        self.to_provider_queue: "_QueueType" = queue.Queue()
         # A primitive is sent to the service user when the DUL service provider
         # adds to the to_user_queue.
         self.to_user_queue: "queue.Queue[_PDUPrimitiveType]" = queue.Queue()
 
+        # A queue storing PDUs received from the peer
+        self._recv_pdu: "queue.Queue[_PDUType]" = queue.Queue()
+
         # Set the (network) idle and ARTIM timers
         # Timeouts gets set after DUL init so these are temporary
         self._idle_timer = Timer(60)
@@ -114,17 +113,6 @@ class DULServiceProvider(Thread):
         """Return the parent :class:`~pynetdicom.association.Association`."""
         return self._assoc
 
-    def _check_incoming_primitive(self) -> bool:
-        """Check the incoming primitive."""
-        try:
-            # Check the queue and see if there are any primitives
-            # If so then put the corresponding event on the event queue
-            self.primitive = self.to_provider_queue.get(False)
-            self.event_queue.put(self._primitive_to_event(self.primitive))
-            return True
-        except queue.Empty:
-            return False
-
     def _decode_pdu(self, bytestream: bytearray) -> Tuple[_PDUType, str]:
         """Decode a received PDU.
 
@@ -209,48 +197,48 @@ class DULServiceProvider(Thread):
         except (queue.Empty, IndexError):
             return None
 
-    @staticmethod
-    def _primitive_to_event(primitive: _PDUPrimitiveType) -> str:
-        """Returns the state machine event associated with sending a primitive.
-
-        Parameters
-        ----------
-        primitive : pdu_primitives.ServiceParameter
-            The Association primitive
+    def _process_recv_primitive(self) -> bool:
+        """Check to see if the local user has sent any primitives to the DUL"""
+        # Check the queue and see if there are any primitives
+        # If so then put the corresponding event on the event queue
+        try:
+            primitive = self.to_provider_queue.queue[0]
+        except (queue.Empty, IndexError):
+            return False
 
-        Returns
-        -------
-        str
-            The event associated with the primitive
-        """
-        if isinstance(primitive, A_ASSOCIATE):
+        if isinstance(primitive, T_CONNECT):
+            # Evt2 or Evt17, depending on whether successful or not
+            event = primitive.result
+        elif isinstance(primitive, A_ASSOCIATE):
             if primitive.result is None:
                 # A-ASSOCIATE Request
-                return "Evt1"
-
-            if primitive.result == 0x00:
+                event = "Evt1"
+            elif primitive.result == 0x00:
                 # A-ASSOCIATE Response (accept)
-                return "Evt7"
-
-            # A-ASSOCIATE Response (reject)
-            return "Evt8"
-
-        if isinstance(primitive, A_RELEASE):
+                event = "Evt7"
+            else:
+                # A-ASSOCIATE Response (reject)
+                event = "Evt8"
+        elif isinstance(primitive, A_RELEASE):
             if primitive.result is None:
                 # A-Release Request
-                return "Evt11"
-
-            # A-Release Response
-            # result is 'affirmative'
-            return "Evt14"
-
-        if isinstance(primitive, (A_ABORT, A_P_ABORT)):
-            return "Evt15"
-
-        if isinstance(primitive, P_DATA):
-            return "Evt9"
-
-        raise ValueError("_primitive_to_event(): invalid primitive")
+                event = "Evt11"
+            else:
+                # A-Release Response
+                # result is 'affirmative'
+                event = "Evt14"
+        elif isinstance(primitive, (A_ABORT, A_P_ABORT)):
+            event = "Evt15"
+        elif isinstance(primitive, P_DATA):
+            event = "Evt9"
+        else:
+            raise ValueError(
+                f"Unknown primitive type '{primitive.__class__.__name__}' received"
+            )
+
+        self.event_queue.put(event)
+
+        return True
 
     def _read_pdu_data(self) -> None:
         """Read PDU data sent by the peer from the socket.
@@ -324,19 +312,16 @@ class DULServiceProvider(Thread):
             LOGGER.exception(exc)
             # Evt19: Unrecognised or invalid PDU received
             self.event_queue.put("Evt19")
-            self.pdu = None
-            self.primitive = None
             return
 
-        self.pdu = pdu
-        self.primitive = self.pdu.to_primitive()
+        self._recv_pdu.put(pdu)
 
     def receive_pdu(
         self, wait: bool = False, timeout: Optional[float] = None
     ) -> Optional[_PDUPrimitiveType]:
         """Return an item from the queue if one is available.
 
-        Get the next item to be processed out of the queue of items sent
+        Get the next service primitive to be processed out of the queue of items sent
         from the DUL service provider to the service user
 
         Parameters
@@ -351,10 +336,9 @@ class DULServiceProvider(Thread):
 
         Returns
         -------
-        object
-            The next object in the :attr:`~DULServiceProvider.to_user_queue`.
-        None
-            If the queue is empty.
+        Optional[Union[A_ASSOCIATE, A_RELEASE, A_ABORT, A_P_ABORT]]
+            The next primitive in the :attr:`~DULServiceProvider.to_user_queue`, or
+            ``None`` if the queue is empty.
         """
         try:
             # If block is True and timeout is None then block until an item
@@ -363,14 +347,13 @@ class DULServiceProvider(Thread):
             #   raises queue.Empty if no item was available in that time.
             # If block is False, return an item if one is immediately
             #   available, otherwise raise queue.Empty
-            queue_item = self.to_user_queue.get(block=wait, timeout=timeout)
+            primitive = self.to_user_queue.get(block=wait, timeout=timeout)
 
             # Event handler - ACSE received primitive from DUL service
-            acse_primitives = (A_ASSOCIATE, A_RELEASE, A_ABORT, A_P_ABORT)
-            if isinstance(queue_item, acse_primitives):
-                evt.trigger(self.assoc, evt.EVT_ACSE_RECV, {"primitive": queue_item})
+            if isinstance(primitive, (A_ASSOCIATE, A_RELEASE, A_ABORT, A_P_ABORT)):
+                evt.trigger(self.assoc, evt.EVT_ACSE_RECV, {"primitive": primitive})
 
-            return queue_item
+            return primitive
         except queue.Empty:
             return None
 
@@ -414,9 +397,9 @@ class DULServiceProvider(Thread):
             try:
                 # We can either encode and send a primitive **OR**
                 #   receive and decode a PDU per loop of the reactor
-                if self._check_incoming_primitive():
+                if self._process_recv_primitive():  # encode (sent by state machine)
                     pass
-                elif self._is_transport_event():
+                elif self._is_transport_event():  # receive and decode PDU
                     self._idle_timer.restart()
             except Exception as exc:
                 LOGGER.error("Exception in DUL.run(), aborting association")
@@ -447,6 +430,18 @@ class DULServiceProvider(Thread):
             self.state_machine.do_action(event)
             sleep = False
 
+    def _send(self, pdu: _PDUType) -> None:
+        """Encode and send a PDU to the peer.
+
+        Parameters
+        ----------
+        pdu : pynetdicom.pdu.PDU
+            The PDU to be encoded and sent to the peer.
+        """
+        sock = cast("AssociationSocket", self.socket)
+        sock.send(pdu.encode())
+        evt.trigger(self.assoc, evt.EVT_PDU_SENT, {"pdu": pdu})
+
     def send_pdu(self, primitive: _PDUPrimitiveType) -> None:
         """Place a primitive in the provider queue to be sent to the peer.
 
@@ -467,8 +462,7 @@ class DULServiceProvider(Thread):
             * :class:`P_DATA`
         """
         # Event handler - ACSE sent primitive to the DUL service
-        acse_primitives = (A_ASSOCIATE, A_RELEASE, A_ABORT, A_P_ABORT)
-        if isinstance(primitive, acse_primitives):
+        if isinstance(primitive, (A_ASSOCIATE, A_RELEASE, A_ABORT, A_P_ABORT)):
             evt.trigger(self.assoc, evt.EVT_ACSE_SENT, {"primitive": primitive})
 
         self.to_provider_queue.put(primitive)
diff --git a/pynetdicom/fsm.py b/pynetdicom/fsm.py
index d1670ae49..289a4fc12 100644
--- a/pynetdicom/fsm.py
+++ b/pynetdicom/fsm.py
@@ -2,6 +2,7 @@
 The DUL's finite state machine representation.
 """
 import logging
+import queue
 from typing import TYPE_CHECKING, cast, Tuple
 
 from pynetdicom import evt
@@ -14,12 +15,13 @@ from pynetdicom.pdu import (
     A_RELEASE_RP,
     A_ABORT_RQ,
 )
-from pynetdicom.pdu_primitives import A_P_ABORT
+from pynetdicom.pdu_primitives import A_P_ABORT, A_ABORT
+from pynetdicom.transport import T_CONNECT
 
 if TYPE_CHECKING:  # pragma: no cover
     from pynetdicom.dul import DULServiceProvider
     from pynetdicom.transport import AssociationSocket
-    from pynetdicom.pdu_primitives import A_ASSOCIATE, P_DATA, A_RELEASE, A_ABORT
+    from pynetdicom.pdu_primitives import A_ASSOCIATE, P_DATA, A_RELEASE
 
 
 LOGGER = logging.getLogger("pynetdicom.sm")
@@ -99,10 +101,13 @@ class StateMachine:
                 },
             )
             # print(
-            #    "{}: {} + {} -> {} -> {}".format(
-            #        self.dul.assoc.mode[0].upper(), self.current_state,
-            #        event, action_name, next_state
-            #    )
+            #     "{}: {} + {} -> {} -> {}".format(
+            #         self.dul.assoc.mode[0].upper(),
+            #         self.current_state,
+            #         event,
+            #         action_name,
+            #         next_state,
+            #     )
             # )
 
             # Move the state machine to the next state
@@ -166,12 +171,12 @@ def AE_1(dul: "DULServiceProvider") -> str:
     str
         ``'Sta4'``, the next state of the state machine.
     """
+    # A-ASSOCIATE (request) primitive received from local user
+    request = cast("A_ASSOCIATE", dul.to_provider_queue.get(False))
+
     # Issue TRANSPORT CONNECT request primitive to local transport service
-    # This is our "TRANSPORT CONNECT" primitive - it attempts to connect
-    #   to the peer, emitting either Evt2 or Evt17
     sock = cast("AssociationSocket", dul.socket)
-    primitive = cast("A_ASSOCIATE", dul.primitive)
-    sock.connect(cast(Tuple[str, int], primitive.called_presentation_address))
+    sock.connect(T_CONNECT(request))
 
     return "Sta4"
 
@@ -194,13 +199,11 @@ def AE_2(dul: "DULServiceProvider") -> str:
     str
         ``'Sta5'``, the next state of the state machine.
     """
-    # Send A-ASSOCIATE-RQ PDU
-    dul.pdu = A_ASSOCIATE_RQ()
-    dul.pdu.from_primitive(cast("A_ASSOCIATE", dul.primitive))
+    # TRANSPORT CONNECTION primitive received from transport service
+    primitive = cast("T_CONNECT", dul.to_provider_queue.get(False))
 
-    sock = cast("AssociationSocket", dul.socket)
-    sock.send(dul.pdu.encode())
-    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": dul.pdu})
+    # Send A-ASSOCIATE-RQ PDU to the peer
+    dul._send(A_ASSOCIATE_RQ(primitive.request))
 
     return "Sta5"
 
@@ -222,8 +225,11 @@ def AE_3(dul: "DULServiceProvider") -> str:
     str
         ``'Sta6'``, the next state of the state machine
     """
+    # Received A-ASSOCIATE-AC PDU from the peer
+    pdu = dul._recv_pdu.get(False)
+
     # Issue A-ASSOCIATE confirmation (accept) primitive
-    dul.to_user_queue.put(cast("A_ASSOCIATE", dul.primitive))
+    dul.to_user_queue.put(cast("A_ASSOCIATE", pdu.to_primitive()))
 
     return "Sta6"
 
@@ -246,9 +252,12 @@ def AE_4(dul: "DULServiceProvider") -> str:
     str
         ``'Sta1'``, the next state of the state machine
     """
+    # Received A-ASSOCIATE-RJ PDU from the peer
+    pdu = dul._recv_pdu.get(False)
+
     # Issue A-ASSOCIATE confirmation (reject) primitive and close transport
     # connection
-    dul.to_user_queue.put(cast("A_ASSOCIATE", dul.primitive))
+    dul.to_user_queue.put(cast("A_ASSOCIATE", pdu.to_primitive()))
     sock = cast("AssociationSocket", dul.socket)
     sock.close()
 
@@ -315,29 +324,24 @@ def AE_6(dul: "DULServiceProvider") -> str:
     # Stop ARTIM timer
     dul.artim_timer.stop()
 
+    recv_pdu = cast(A_ASSOCIATE_RQ, dul._recv_pdu.get(False))
+    primitive = recv_pdu.to_primitive()
+
     # If A-ASSOCIATE-RQ not acceptable by service dul provider
     #   Then set reason and send -RJ PDU back to peer
-    pdu = cast(A_ASSOCIATE_RQ, dul.pdu)
-    primitive = cast("A_ASSOCIATE", dul.primitive)
-    if pdu.protocol_version != 0x0001:
+    if recv_pdu.protocol_version != 0x0001:
         LOGGER.error(
             "A-ASSOCIATE-RQ: Unsupported protocol version "
-            f"'0x{pdu.protocol_version:04X}'"
+            f"'0x{recv_pdu.protocol_version:04X}'"
         )
 
         # Send A-ASSOCIATE-RJ PDU and start ARTIM timer
-        # dul.primitive is A_ASSOCIATE
-
         primitive.result = 0x01
         primitive.result_source = 0x02
         primitive.diagnostic = 0x02
 
-        dul.pdu = A_ASSOCIATE_RJ()
-        dul.pdu.from_primitive(primitive)
+        dul._send(A_ASSOCIATE_RJ(primitive))
 
-        sock = cast("AssociationSocket", dul.socket)
-        sock.send(dul.pdu.encode())
-        evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": dul.pdu})
         dul.artim_timer.start()
 
         return "Sta13"
@@ -366,13 +370,11 @@ def AE_7(dul: "DULServiceProvider") -> str:
     str
         ``'Sta6'``, the next state of the state machine
     """
-    # Send A-ASSOCIATE-AC PDU
-    dul.pdu = A_ASSOCIATE_AC()
-    dul.pdu.from_primitive(cast("A_ASSOCIATE", dul.primitive))
+    # Received A-ASSOCIATE (AC) primitive from local user
+    primitive = cast("A_ASSOCIATE", dul.to_provider_queue.get(False))
 
-    sock = cast("AssociationSocket", dul.socket)
-    sock.send(dul.pdu.encode())
-    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": dul.pdu})
+    # Send A-ASSOCIATE-AC PDU
+    dul._send(A_ASSOCIATE_AC(primitive))
 
     return "Sta6"
 
@@ -394,13 +396,11 @@ def AE_8(dul: "DULServiceProvider") -> str:
     str
         ``'Sta13'``, the next state of the state machine
     """
-    # Send A-ASSOCIATE-RJ PDU and start ARTIM timer
-    dul.pdu = A_ASSOCIATE_RJ()
-    dul.pdu.from_primitive(cast("A_ASSOCIATE", dul.primitive))
+    # Received A-ASSOCIATE (RJ) primitive from local user
+    primitive = cast("A_ASSOCIATE", dul.to_provider_queue.get(False))
 
-    sock = cast("AssociationSocket", dul.socket)
-    sock.send(dul.pdu.encode())
-    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": dul.pdu})
+    # Send A-ASSOCIATE-RJ PDU and start ARTIM timer
+    dul._send(A_ASSOCIATE_RJ(primitive))
     dul.artim_timer.start()
 
     return "Sta13"
@@ -423,14 +423,11 @@ def DT_1(dul: "DULServiceProvider") -> str:
     str
         ``'Sta6'``, the next state of the state machine
     """
-    # Send P-DATA-TF PDU
-    dul.pdu = P_DATA_TF()
-    dul.pdu.from_primitive(cast("P_DATA", dul.primitive))
-    dul.primitive = None  # Why this?
+    # P-DATA request received from local user
+    primitive = cast("P_DATA", dul.to_provider_queue.get(False))
 
-    sock = cast("AssociationSocket", dul.socket)
-    sock.send(dul.pdu.encode())
-    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": dul.pdu})
+    # Send P-DATA-TF PDU
+    dul._send(P_DATA_TF(primitive))
 
     return "Sta6"
 
@@ -452,8 +449,11 @@ def DT_2(dul: "DULServiceProvider") -> str:
     str
         ``'Sta6'``, the next state of the state machine
     """
+    # P-DATA-TF PDU received from peer
+    pdu = dul._recv_pdu.get(False)
+
     # Send P-DATA indication primitive directly to DIMSE for processing
-    dul.assoc.dimse.receive_primitive(cast("P_DATA", dul.primitive))
+    dul.assoc.dimse.receive_primitive(cast("P_DATA", pdu.to_primitive()))
 
     return "Sta6"
 
@@ -475,13 +475,11 @@ def AR_1(dul: "DULServiceProvider") -> str:
     str
         ``'Sta7'``, the next state of the state machine
     """
-    # Send A-RELEASE-RQ PDU
-    dul.pdu = A_RELEASE_RQ()
-    dul.pdu.from_primitive(cast("A_RELEASE", dul.primitive))
+    # Received A-RELEASE request from local user
+    primitive = cast("A_RELEASE", dul.to_provider_queue.get(False))
 
-    sock = cast("AssociationSocket", dul.socket)
-    sock.send(dul.pdu.encode())
-    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": dul.pdu})
+    # Send A-RELEASE-RQ PDU
+    dul._send(A_RELEASE_RQ(primitive))
 
     return "Sta7"
 
@@ -503,8 +501,11 @@ def AR_2(dul: "DULServiceProvider") -> str:
     str
         ``'Sta8'``, the next state of the state machine
     """
+    # A-RELEASE-RQ PDU received from peer
+    pdu = dul._recv_pdu.get(False)
+
     # Send A-RELEASE indication primitive
-    dul.to_user_queue.put(cast("A_RELEASE", dul.primitive))
+    dul.to_user_queue.put(pdu.to_primitive())
 
     return "Sta8"
 
@@ -527,8 +528,11 @@ def AR_3(dul: "DULServiceProvider") -> str:
     str
         ``'Sta1'``, the next state of the state machine
     """
+    # A-RELEASE-RP PDU received from peer
+    pdu = dul._recv_pdu.get(False)
+
     # Issue A-RELEASE confirmation primitive and close transport connection
-    dul.to_user_queue.put(cast("A_RELEASE", dul.primitive))
+    dul.to_user_queue.put(pdu.to_primitive())
     sock = cast("AssociationSocket", dul.socket)
     sock.close()
 
@@ -560,13 +564,11 @@ def AR_4(dul: "DULServiceProvider") -> str:
     str
         ``'Sta13'``, the next state of the state machine
     """
-    # Issue A-RELEASE-RP PDU and start ARTIM timer
-    dul.pdu = A_RELEASE_RP()
-    dul.pdu.from_primitive(cast("A_RELEASE", dul.primitive))
+    # A-RELEASE (response) primitive received from local user
+    primitive = cast("A_RELEASE", dul.to_provider_queue.get(False))
 
-    sock = cast("AssociationSocket", dul.socket)
-    sock.send(dul.pdu.encode())
-    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": dul.pdu})
+    # Issue A-RELEASE-RP PDU and start ARTIM timer
+    dul._send(A_RELEASE_RP(primitive))
     dul.artim_timer.start()
 
     return "Sta13"
@@ -621,8 +623,11 @@ def AR_6(dul: "DULServiceProvider") -> str:
     str
         ``'Sta7'``, the next state of the state machine
     """
+    # P-DATA-TF PDU received from peer
+    pdu = cast("P_DATA_TF", dul._recv_pdu.get(False))
+
     # Issue P-DATA indication
-    dul.to_user_queue.put(cast("A_RELEASE", dul.primitive))
+    dul.to_user_queue.put(pdu.to_primitive())
 
     return "Sta7"
 
@@ -645,13 +650,11 @@ def AR_7(dul: "DULServiceProvider") -> str:
     str
         ``'Sta8'``, the next state of the state machine
     """
-    # Issue P-DATA-TF PDU
-    dul.pdu = P_DATA_TF()
-    dul.pdu.from_primitive(cast("P_DATA", dul.primitive))
+    # P-DATA primitive received from local user
+    primitive = cast("P_DATA", dul.to_provider_queue.get(False))
 
-    sock = cast("AssociationSocket", dul.socket)
-    sock.send(dul.pdu.encode())
-    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": dul.pdu})
+    # Issue P-DATA-TF PDU
+    dul._send(P_DATA_TF(primitive))
 
     return "Sta8"
 
@@ -674,8 +677,11 @@ def AR_8(dul: "DULServiceProvider") -> str:
     str
         Either ``'Sta9'`` or ``'Sta10'``, the next state of the state machine
     """
+    # A-RELEASE-RQ PDU received from peer
+    pdu = cast("A_RELEASE_RQ", dul._recv_pdu.get(False))
+
     # Issue A-RELEASE indication (release collision)
-    dul.to_user_queue.put(cast("A_RELEASE", dul.primitive))
+    dul.to_user_queue.put(pdu.to_primitive())
     if dul.assoc.is_requestor:
         return "Sta9"
 
@@ -699,13 +705,11 @@ def AR_9(dul: "DULServiceProvider") -> str:
     str
         ``'Sta11'``, the next state of the state machine
     """
-    # Send A-RELEASE-RP PDU
-    dul.pdu = A_RELEASE_RP()
-    dul.pdu.from_primitive(cast("A_RELEASE", dul.primitive))
+    # A-RELEASE (response) primitive received from local user
+    primitive = cast("A_RELEASE", dul.to_provider_queue.get(False))
 
-    sock = cast("AssociationSocket", dul.socket)
-    sock.send(dul.pdu.encode())
-    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": dul.pdu})
+    # Send A-RELEASE-RP PDU
+    dul._send(A_RELEASE_RP(primitive))
 
     return "Sta11"
 
@@ -727,8 +731,11 @@ def AR_10(dul: "DULServiceProvider") -> str:
     str
         ``'Sta12'``, the next state of the state machine
     """
+    # A-RELEASE-RP PDU received from peer
+    pdu = cast("A_RELEASE_RP", dul._recv_pdu.get(False))
+
     # Issue A-RELEASE confirmation primitive
-    dul.to_user_queue.put(cast("A_RELEASE", dul.primitive))
+    dul.to_user_queue.put(pdu.to_primitive())
 
     return "Sta12"
 
@@ -752,17 +759,25 @@ def AA_1(dul: "DULServiceProvider") -> str:
     str
         ``'Sta13'``, the next state of the state machine
     """
+    # Received invalid PDU from peer or an A-ABORT primitive from local user
+    try:
+        primitive = dul.to_provider_queue.queue[0]
+        if isinstance(primitive, (A_ABORT, A_P_ABORT)):
+            primitive = dul.to_provider_queue.get(False)
+    except (queue.Empty, IndexError):
+        primitive = None
+
     # Send A-ABORT PDU (service-user source) and start (or restart
     # if already started) ARTIM timer.
-    dul.pdu = A_ABORT_RQ()
-    dul.pdu.source = 0x00
-    # Reason not specified
-    dul.pdu.reason_diagnostic = 0x00
-    dul.pdu.from_primitive(cast("A_ABORT", dul.primitive))
-
-    sock = cast("AssociationSocket", dul.socket)
-    sock.send(dul.pdu.encode())
-    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": dul.pdu})
+    pdu = A_ABORT_RQ()
+    if primitive is not None:
+        pdu.from_primitive(primitive)
+    else:
+        # Reason not specified
+        pdu.source = 0x00
+        pdu.reason_diagnostic = 0x00
+
+    dul._send(pdu)
     dul.artim_timer.restart()
 
     return "Sta13"
@@ -822,12 +837,15 @@ def AA_3(dul: "DULServiceProvider") -> str:
     str
         ``'Sta1'``, the next state of the state machine
     """
+    # A-ABORT PDU received from peer
+    pdu = cast("A_ABORT_RQ", dul._recv_pdu.get(False))
+
     # If (service-user initiated abort):
     #   - Issue A-ABORT indication and close transport connection.
     # Otherwise (service-dul initiated abort):
     #   - Issue A-P-ABORT indication and close transport connection.
     # This action is triggered by the reception of an A-ABORT PDU
-    dul.to_user_queue.put(cast("A_ABORT", dul.primitive))
+    dul.to_user_queue.put(pdu.to_primitive())
     sock = cast("AssociationSocket", dul.socket)
     sock.close()
 
@@ -926,7 +944,10 @@ def AA_6(dul: "DULServiceProvider") -> str:
         ``'Sta13'``, the next state of the state machine
     """
     # Ignore PDU
-    dul.primitive = None
+    try:
+        dul._recv_pdu.get(False)
+    except queue.Empty:
+        pass
 
     return "Sta13"
 
@@ -953,12 +974,7 @@ def AA_7(dul: "DULServiceProvider") -> str:
     primitive.provider_reason = 0x02
 
     # Send A-ABORT PDU
-    pdu = A_ABORT_RQ()
-    pdu.from_primitive(primitive)
-
-    sock = cast("AssociationSocket", dul.socket)
-    sock.send(pdu.encode())
-    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": pdu})
+    dul._send(A_ABORT_RQ(primitive))
 
     return "Sta13"
 
@@ -986,14 +1002,12 @@ def AA_8(dul: "DULServiceProvider") -> str:
     """
     # Send A-ABORT PDU (service-dul source), issue A-P-ABORT
     # indication, and start ARTIM timer.
-    # Send A-ABORT PDU
     pdu = A_ABORT_RQ()
     pdu.source = 0x02  # A-P-ABORT
     pdu.reason_diagnostic = 0x00
 
-    sock = cast("AssociationSocket", dul.socket)
-    sock.send(pdu.encode())
-    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {"pdu": pdu})
+    # Send A-ABORT PDU
+    dul._send(pdu)
 
     # Issue A-P-ABORT to user
     primitive = A_P_ABORT()
diff --git a/pynetdicom/pdu.py b/pynetdicom/pdu.py
index 978b5c0ad..4108b105d 100644
--- a/pynetdicom/pdu.py
+++ b/pynetdicom/pdu.py
@@ -443,8 +443,14 @@ class A_ASSOCIATE_RQ(PDU):
       and :dcm:`9.3.1<part08/sect_9.3.html#sect_9.3.1>`
     """
 
-    def __init__(self) -> None:
-        """Initialise a new A-ASSOCIATE-RQ PDU."""
+    def __init__(self, primitive: Optional["A_ASSOCIATE"] = None) -> None:
+        """Initialise a new A-ASSOCIATE-RQ PDU.
+
+        Parameters
+        ----------
+        primitive : pynetdicom.pdu_primitive.A_ASSOCIATE
+            The primitive to use to initialise the PDU.
+        """
         # We allow the user to modify the protocol version if so desired
         self.protocol_version = 0x01
         self._called_aet = ""
@@ -461,6 +467,9 @@ class A_ASSOCIATE_RQ(PDU):
         # The order of the items in the list may not be as given above
         self.variable_items: _PDUItemType = []
 
+        if primitive is not None:
+            self.from_primitive(primitive)
+
     def from_primitive(self, primitive: "A_ASSOCIATE") -> None:
         """Setup the current PDU using an A-ASSOCIATE (request) primitive.
 
@@ -818,8 +827,14 @@ class A_ASSOCIATE_AC(PDU):
       :dcm:`Section 9.3.1<part08/sect_9.3.html#sect_9.3.1>`
     """
 
-    def __init__(self) -> None:
-        """Initialise a new A-ASSOCIATE-AC PDU."""
+    def __init__(self, primitive: Optional["A_ASSOCIATE"] = None) -> None:
+        """Initialise a new A-ASSOCIATE-AC PDU.
+
+        Parameters
+        ----------
+        primitive : pynetdicom.pdu_primitive.A_ASSOCIATE
+            The primitive to use to initialise the PDU.
+        """
         # We allow the user to modify the protocol version if so desired
         self.protocol_version = 0x01
         # Called AE Title, should be present, but no guarantees
@@ -834,6 +849,9 @@ class A_ASSOCIATE_AC(PDU):
         # The order of the items in the list may not be as given above
         self.variable_items: _PDUItemType = []
 
+        if primitive is not None:
+            self.from_primitive(primitive)
+
     def from_primitive(self, primitive: "A_ASSOCIATE") -> None:
         """Setup the current PDU using an A-ASSOCIATE (accept) primitive.
 
@@ -1197,12 +1215,21 @@ class A_ASSOCIATE_RJ(PDU):
       :dcm:`Section 9.3.1<part08/sect_9.3.html#sect_9.3.1>`
     """
 
-    def __init__(self) -> None:
-        """Initialise a new A-ASSOCIATE-RJ PDU."""
+    def __init__(self, primitive: Optional["A_ASSOCIATE"] = None) -> None:
+        """Initialise a new A-ASSOCIATE-RJ PDU.
+
+        Parameters
+        ----------
+        primitive : pynetdicom.pdu_primitive.A_ASSOCIATE
+            The primitive to use to initialise the PDU.
+        """
         self.result: Optional[int] = None
         self.source: Optional[int] = None
         self.reason_diagnostic: Optional[int] = None
 
+        if primitive is not None:
+            self.from_primitive(primitive)
+
     def from_primitive(self, primitive: "A_ASSOCIATE") -> None:
         """Setup the current PDU using an A-ASSOCIATE (reject) primitive.
 
@@ -1412,10 +1439,19 @@ class P_DATA_TF(PDU):
       :dcm:`Section 9.3.1<part08/sect_9.3.html#sect_9.3.1>`
     """
 
-    def __init__(self) -> None:
-        """Initialise a new P-DATA-TF PDU."""
+    def __init__(self, primitive: Optional["P_DATA"] = None) -> None:
+        """Initialise a new P-DATA-TF PDU.
+
+        Parameters
+        ----------
+        primitive : pynetdicom.pdu_primitive.P_DATA
+            The primitive to use to initialise the PDU.
+        """
         self.presentation_data_value_items: _PDVItem = []
 
+        if primitive is not None:
+            self.from_primitive(primitive)
+
     def from_primitive(self, primitive: "P_DATA") -> None:
         """Setup the current PDU using a P-DATA primitive.
 
@@ -1638,9 +1674,16 @@ class A_RELEASE_RQ(PDU):
       :dcm:`Section 9.3.1<part08/sect_9.3.html#sect_9.3.1>`
     """
 
-    def __init__(self) -> None:
-        """Initialise a new A-RELEASE-RQ PDU."""
-        pass
+    def __init__(self, primitive: Optional["A_RELEASE"] = None) -> None:
+        """Initialise a new A-RELEASE-RQ PDU.
+
+        Parameters
+        ----------
+        primitive : pynetdicom.pdu_primitive.A_RELEASE
+            The primitive to use to initialise the PDU.
+        """
+        if primitive is not None:
+            self.from_primitive(primitive)
 
     @staticmethod
     def from_primitive(primitive: "A_RELEASE") -> None:
@@ -1763,9 +1806,16 @@ class A_RELEASE_RP(PDU):
       :dcm:`Section 9.3.1<part08/sect_9.3.html#sect_9.3.1>`
     """
 
-    def __init__(self) -> None:
-        """Initialise a new A-RELEASE-RP PDU."""
-        pass
+    def __init__(self, primitive: Optional["A_RELEASE"] = None) -> None:
+        """Initialise a new A-RELEASE-RP PDU.
+
+        Parameters
+        ----------
+        primitive : pynetdicom.pdu_primitive.A_RELEASE
+            The primitive to use to initialise the PDU.
+        """
+        if primitive is not None:
+            self.from_primitive(primitive)
 
     @staticmethod
     def from_primitive(primitive: "A_RELEASE") -> None:
@@ -1902,11 +1952,22 @@ class A_ABORT_RQ(PDU):
       :dcm:`Section 9.3.1<part08/sect_9.3.html#sect_9.3.1>`
     """
 
-    def __init__(self) -> None:
-        """Initialise a new A-ABORT-RQ PDU."""
+    def __init__(
+        self, primitive: Optional[Union["A_ABORT", "A_P_ABORT"]] = None
+    ) -> None:
+        """Initialise a new A-ABORT-RQ PDU.
+
+        Parameters
+        ----------
+        primitive : pynetdicom.pdu_primitive.A_ABORT
+            The primitive to use to initialise the PDU.
+        """
         self.source: Optional[int] = None
         self.reason_diagnostic: Optional[int] = None
 
+        if primitive is not None:
+            self.from_primitive(primitive)
+
     def from_primitive(self, primitive: _AbortType) -> None:
         """Setup the current PDU using an A-ABORT or A-P-ABORT primitive.
 
diff --git a/pynetdicom/transport.py b/pynetdicom/transport.py
index d14bcb51e..1581db755 100644
--- a/pynetdicom/transport.py
+++ b/pynetdicom/transport.py
@@ -37,16 +37,59 @@ from pynetdicom._handlers import (
     standard_pdu_recv_handler,
     standard_pdu_sent_handler,
 )
+from pynetdicom.pdu_primitives import A_ASSOCIATE
 from pynetdicom.presentation import PresentationContext
 
 if TYPE_CHECKING:  # pragma: no cover
     from pynetdicom.ae import ApplicationEntity
     from pynetdicom.association import Association
+    from pynetdicom.dul import _QueueType
 
 
 LOGGER = logging.getLogger("pynetdicom.transport")
 
 
+class T_CONNECT:
+    """A TRANSPORT CONNECTION primitive
+
+    .. versionadded:: 2.0
+    """
+
+    def __init__(self, address: Union[Tuple[str, int], "A_ASSOCIATE"]) -> None:
+        """Create a new TRANSPORT CONNECTION primitive.
+
+        Parameters
+        ----------
+        address : Union[Tuple[str, int], pynetdicom.pdu_primitives.A_ASSOCIATE]
+            The ``(str: IP address, int: port)`` or A-ASSOCIATE (request) primitive to
+            use when making a connection with a peer.
+        """
+        self._request = None
+        self.result = ""
+
+        if isinstance(address, tuple):
+            self._address = address
+        elif isinstance(address, A_ASSOCIATE):
+            self._address = cast(Tuple[str, int], address.called_presentation_address)
+            self._request = address
+        else:
+            raise TypeError(
+                f"'address' must be 'Tuple[str, int]' or "
+                "'pynetdicom.pdu_primitives.A_ASSOCIATE', not "
+                f"'{address.__class__.__name__}'"
+            )
+
+    @property
+    def address(self) -> Tuple[str, int]:
+        """Return the peer's ``(str: IP address, int: port)``."""
+        return self._address
+
+    @property
+    def request(self) -> Optional[A_ASSOCIATE]:
+        """Return the A-ASSOCIATE (request) primitive, or ``None`` if not available."""
+        return self._request
+
+
 class AssociationSocket:
     """A wrapper for a `socket
     <https://docs.python.org/3/library/socket.html#socket-objects>`_ object.
@@ -147,18 +190,18 @@ class AssociationSocket:
         # Evt17: Transport connection closed
         self.event_queue.put("Evt17")
 
-    def connect(self, address: Tuple[str, int]) -> None:
+    def connect(self, primitive: T_CONNECT) -> None:
         """Try and connect to a remote at `address`.
 
-        **Events Emitted**
+        .. versionchanged:: 2.0
 
-        - Evt2: Transport connection confirmed
-        - Evt17: Transport connection closed
+            Changed to take a :class:`~pynetdicom.transport.T_CONNECT` primitive rather
+            than an address tuple.
 
         Parameters
         ----------
-        address : 2-tuple
-            The ``(host: str, port: int)`` IPv4 address to connect to.
+        primitive : pynetdicom.transport.T_CONNECT
+            The TRANSPORT CONNECT primitive to use when connecting to a peer.
         """
         if self.socket is None:
             self.socket = self._create_socket()
@@ -178,14 +221,15 @@ class AssociationSocket:
             self.socket.settimeout(self.assoc.connection_timeout)
             # Try and connect to remote at (address, port)
             #   raises socket.error if connection refused
-            self.socket.connect(address)
+            self.socket.connect(primitive.address)
             # Clear ae connection timeout
             self.socket.settimeout(None)
             # Trigger event - connection open
-            evt.trigger(self.assoc, evt.EVT_CONN_OPEN, {"address": address})
+            evt.trigger(self.assoc, evt.EVT_CONN_OPEN, {"address": primitive.address})
             self._is_connected = True
             # Evt2: Transport connection confirmation
-            self.event_queue.put("Evt2")
+            primitive.result = "Evt2"
+            self.provider_queue.put(primitive)
         except OSError as exc:
             # Log connection failure
             LOGGER.error("Association request failed: unable to connect to remote")
@@ -203,7 +247,9 @@ class AssociationSocket:
                     pass
                 self.socket.close()
                 self.socket = None
-            self.event_queue.put("Evt17")
+
+            primitive.result = "Evt17"
+            self.provider_queue.put(primitive)
         finally:
             self._ready.set()
 
@@ -255,7 +301,7 @@ class AssociationSocket:
 
     @property
     def event_queue(self) -> "queue.Queue[str]":
-        """Return the :class:`~pynetdicom.association.Association`'s event
+        """Return the :class:`~pynetdicom.association.Association`'s service event
         queue.
         """
         return self.assoc.dul.event_queue
@@ -280,6 +326,13 @@ class AssociationSocket:
 
         return addr
 
+    @property
+    def provider_queue(self) -> "_QueueType":
+        """Return the :class:`~pynetdicom.association.Association`'s service provider
+        queue.
+        """
+        return self.assoc.dul.to_provider_queue
+
     @property
     def ready(self) -> bool:
         """Return ``True`` if there is data available to be read.


Test patch:
diff --git a/pynetdicom/tests/parrot.py b/pynetdicom/tests/parrot.py
index 7028f71fc..27f9f1cb3 100644
--- a/pynetdicom/tests/parrot.py
+++ b/pynetdicom/tests/parrot.py
@@ -1,6 +1,7 @@
 """The Parrot testing server."""
 
 import gc
+import logging
 import select
 import socket
 from socketserver import TCPServer, ThreadingMixIn, BaseRequestHandler
@@ -12,6 +13,9 @@ import time
 from pynetdicom.transport import AssociationServer
 
 
+LOGGER = logging.getLogger("pynetdicom")
+
+
 class ParrotRequest(BaseRequestHandler):
     @property
     def commands(self):
@@ -171,27 +175,29 @@ class SteppingParrotRequest(ParrotRequest):
         self.received = []
         self.sent = []
         while True:
-            # print('Parrot: waiting on threading.Event')
+            LOGGER.debug("Parrot: waiting on event.wait()")
             self.event.wait()
-            # print('Parrot: wait ended')
+            LOGGER.debug("Parrot: event.wait() ended")
             cmd, data = self.commands.pop(0)
-            # print('Parrot: running command', cmd)
+            LOGGER.debug(f"Parrot: running command '{cmd}'")
+
             if cmd == "exit":
-                # print('Parrot: exiting...')
+                LOGGER.debug("   Parrot: exiting...")
                 return
-
-            if cmd == "recv":
-                # print('Parrot: receiving data')
+            elif cmd == "recv":
+                LOGGER.debug("   Parrot: receiving data")
                 self.kill_read = False
                 while not self.kill_read:
                     if self.ready:
                         self.received.append(bytes(self.read_data))
                         self.kill_read = True
+                LOGGER.debug(f"  Parrot: received {len(self.received)} bytes")
             elif cmd == "send":
-                # print('Parrot: sending data')
+                LOGGER.debug("   Parrot: sending data")
                 self.send(data)
                 self.sent.append(data)
 
+            LOGGER.debug("Parrot: step ending, event.clear()")
             self.event.clear()
 
 
@@ -250,7 +256,7 @@ class Parrot(AssociationServer):
 
     def step(self):
         while self.event.is_set():
-            time.sleep(0.05)
+            time.sleep(0.0001)
 
         self.event.set()
 
diff --git a/pynetdicom/tests/test_dul.py b/pynetdicom/tests/test_dul.py
index dabb90820..7c1d98b23 100644
--- a/pynetdicom/tests/test_dul.py
+++ b/pynetdicom/tests/test_dul.py
@@ -87,33 +87,53 @@ class TestDUL:
             if isinstance(thread, ThreadedParrot):
                 thread.shutdown()
 
-    def test_primitive_to_event(self):
-        """Test that parameter returns expected results"""
+    def test_recv_primitive(self):
+        """Test processing received primitives"""
         dul = DULServiceProvider(DummyAssociation())
-        p2e = dul._primitive_to_event
 
         primitive = A_ASSOCIATE()
         primitive.result = None
-        assert p2e(primitive) == "Evt1"
+        dul.to_provider_queue.put(primitive)
+        dul._process_recv_primitive()
+        assert dul.event_queue.get(False) == "Evt1"
         primitive.result = 0
-        assert p2e(primitive) == "Evt7"
+        dul._process_recv_primitive()
+        assert dul.event_queue.get(False) == "Evt7"
         primitive.result = 1
-        assert p2e(primitive) == "Evt8"
+        dul._process_recv_primitive()
+        assert dul.event_queue.get(False) == "Evt8"
+
+        dul.to_provider_queue.get(False)
 
         primitive = A_RELEASE()
         primitive.result = None
-        assert p2e(primitive) == "Evt11"
+        dul.to_provider_queue.put(primitive)
+        dul._process_recv_primitive()
+        assert dul.event_queue.get(False) == "Evt11"
         primitive.result = "affirmative"
-        assert p2e(primitive) == "Evt14"
+        dul._process_recv_primitive()
+        assert dul.event_queue.get(False) == "Evt14"
+
+        dul.to_provider_queue.get(False)
 
         primitive = A_ABORT()
-        assert p2e(primitive) == "Evt15"
+        dul.to_provider_queue.put(primitive)
+        dul._process_recv_primitive()
+        assert dul.event_queue.get(False) == "Evt15"
+
+        dul.to_provider_queue.get(False)
 
         primitive = P_DATA()
-        assert p2e(primitive) == "Evt9"
+        dul.to_provider_queue.put(primitive)
+        dul._process_recv_primitive()
+        assert dul.event_queue.get(False) == "Evt9"
+
+        dul.to_provider_queue.get(False)
 
-        with pytest.raises(ValueError):
-            p2e("TEST")
+        msg = "Unknown primitive type 'str' received"
+        with pytest.raises(ValueError, match=msg):
+            dul.to_provider_queue.put("TEST")
+            dul._process_recv_primitive()
 
     def test_recv_failure_aborts(self):
         """Test connection close during PDU recv causes abort."""
@@ -250,12 +270,16 @@ class TestDUL:
         assert assoc.is_established
 
         scp.step()  # send bad PDU
-        scp.step()  # receive abort
-        assert assoc.is_aborted
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
+        scp.step()  # receive abort
         scp.step()
         scp.shutdown()
 
+        assert assoc.is_aborted
+
     def test_exception_in_reactor(self):
         """Test that an exception being raised in the DUL reactor kills the
         DUL and aborts the association.
@@ -290,7 +314,12 @@ class TestDUL:
         assoc.dul._read_pdu_data = patch_read_pdu
 
         scp.step()
+
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
+
         assert assoc.is_aborted
 
         scp.step()
diff --git a/pynetdicom/tests/test_fsm.py b/pynetdicom/tests/test_fsm.py
index d7d7d2eee..079a9a10f 100644
--- a/pynetdicom/tests/test_fsm.py
+++ b/pynetdicom/tests/test_fsm.py
@@ -24,7 +24,7 @@ from pynetdicom.pdu_primitives import (
     ImplementationClassUIDNotification,
 )
 from pynetdicom.sop_class import Verification
-from pynetdicom.transport import AssociationSocket
+from pynetdicom.transport import AssociationSocket, T_CONNECT
 from .encoded_pdu_items import (
     a_associate_ac,
     a_associate_rq,
@@ -401,14 +401,23 @@ class TestStateBase:
         fsm = self.monkey_patch(assoc.dul.state_machine)
         return assoc, fsm
 
-    def wait_on_state(self, fsm, state, timeout=5):
+    def wait_on_state(self, fsm, state, timeout=2):
         start = 0
-        while fsm.current_state != state and start < timeout:
-            time.sleep(0.05)
-            start += 0.05
+        while (
+            fsm.current_state != state
+            and fsm.current_state not in state
+            and start < timeout
+        ):
+            time.sleep(0.0001)
+            start += 0.0001
+
+        # if start >= timeout:
+        #     print(f"'wait_on_state' timed out for state {state}")
+
+
+IGNORE_FILTER = "ignore:.*:pytest.PytestUnhandledThreadExceptionWarning"
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState01(TestStateBase):
     """Tests for State 01: Idle."""
 
@@ -443,6 +452,7 @@ class TestState01(TestStateBase):
         # Evt2: Receive TRANSPORT_OPEN from <transport service>
         pass
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt03(self):
         """Test Sta1 + Evt3."""
         # Sta1 + Evt3 -> <ignore> -> Sta1
@@ -456,6 +466,10 @@ class TestState01(TestStateBase):
         self.assoc.dul.socket._is_connected = True
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -463,6 +477,7 @@ class TestState01(TestStateBase):
         assert self.fsm._changes == []
         assert self.fsm._events[:1] == ["Evt3"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt04(self):
         """Test Sta1 + Evt4."""
         # Sta1 + Evt4 -> <ignore> -> Sta1
@@ -476,6 +491,10 @@ class TestState01(TestStateBase):
         self.assoc.dul.socket._is_connected = True
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -492,6 +511,7 @@ class TestState01(TestStateBase):
         #       Start ARTIM timer
         pass
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt06(self):
         """Test Sta1 + Evt6."""
         # Sta1 + Evt6 -> <ignore> -> Sta1
@@ -505,6 +525,10 @@ class TestState01(TestStateBase):
         self.assoc.dul.socket._is_connected = True
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -512,6 +536,7 @@ class TestState01(TestStateBase):
         assert self.fsm._changes == []
         assert self.fsm._events[:1] == ["Evt6"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta1 + Evt7."""
         # Sta1 + Evt7 -> <ignore> -> Sta1
@@ -519,9 +544,12 @@ class TestState01(TestStateBase):
         self.assoc._mode = "acceptor"
         self.assoc.start()
 
+        time.sleep(0.1)
+
         self.assoc.dul.send_pdu(self.get_associate("accept"))
 
-        time.sleep(0.5)
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         self.assoc.kill()
 
@@ -529,6 +557,7 @@ class TestState01(TestStateBase):
         assert self.fsm._changes == []
         assert self.fsm._events[0] == "Evt7"
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta1 + Evt8."""
         # Sta1 + Evt8 -> <ignore> -> Sta1
@@ -536,9 +565,12 @@ class TestState01(TestStateBase):
         self.assoc._mode = "acceptor"
         self.assoc.start()
 
+        time.sleep(0.1)
+
         self.assoc.dul.send_pdu(self.get_associate("reject"))
 
-        time.sleep(0.5)
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         self.assoc.kill()
 
@@ -547,6 +579,7 @@ class TestState01(TestStateBase):
         assert self.fsm._events[0] == "Evt8"
         assert self.fsm.current_state == "Sta1"
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt09(self):
         """Test Sta1 + Evt9."""
         # Sta1 + Evt9 -> <ignore> -> Sta1
@@ -554,9 +587,12 @@ class TestState01(TestStateBase):
         self.assoc._mode = "acceptor"
         self.assoc.start()
 
+        time.sleep(0.1)
+
         self.assoc.dul.send_pdu(self.get_pdata())
 
-        time.sleep(0.5)
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         self.assoc.kill()
 
@@ -565,6 +601,7 @@ class TestState01(TestStateBase):
         assert self.fsm._events[0] == "Evt9"
         assert self.fsm.current_state == "Sta1"
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt10(self):
         """Test Sta1 + Evt10."""
         # Sta1 + Evt10 -> <ignore> -> Sta1
@@ -578,6 +615,10 @@ class TestState01(TestStateBase):
         self.assoc.dul.socket._is_connected = True
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -585,6 +626,7 @@ class TestState01(TestStateBase):
         assert self.fsm._changes == []
         assert self.fsm._events[:1] == ["Evt10"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta1 + Evt11."""
         # Sta1 + Evt11 -> <ignore> -> Sta1
@@ -592,9 +634,12 @@ class TestState01(TestStateBase):
         self.assoc._mode = "acceptor"
         self.assoc.start()
 
+        time.sleep(0.1)
+
         self.assoc.dul.send_pdu(self.get_release(False))
 
-        time.sleep(0.5)
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         self.assoc.kill()
 
@@ -603,6 +648,7 @@ class TestState01(TestStateBase):
         assert self.fsm._events[0] == "Evt11"
         assert self.fsm.current_state == "Sta1"
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt12(self):
         """Test Sta1 + Evt12."""
         # Sta1 + Evt12 -> <ignore> -> Sta1
@@ -616,6 +662,10 @@ class TestState01(TestStateBase):
         self.assoc.dul.socket._is_connected = True
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -623,6 +673,7 @@ class TestState01(TestStateBase):
         assert self.fsm._changes == []
         assert self.fsm._events[:1] == ["Evt12"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt13(self):
         """Test Sta1 + Evt13."""
         # Sta1 + Evt13 -> <ignore> -> Sta1
@@ -636,6 +687,10 @@ class TestState01(TestStateBase):
         self.assoc.dul.socket._is_connected = True
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -643,6 +698,7 @@ class TestState01(TestStateBase):
         assert self.fsm._changes == []
         assert self.fsm._events[:1] == ["Evt13"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt14(self):
         """Test Sta1 + Evt14."""
         # Sta1 + Evt14 -> <ignore> -> Sta1
@@ -650,9 +706,12 @@ class TestState01(TestStateBase):
         self.assoc._mode = "acceptor"
         self.assoc.start()
 
+        time.sleep(0.1)
+
         self.assoc.dul.send_pdu(self.get_release(True))
 
-        time.sleep(0.5)
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         self.assoc.kill()
 
@@ -661,6 +720,7 @@ class TestState01(TestStateBase):
         assert self.fsm._events[0] == "Evt14"
         assert self.fsm.current_state == "Sta1"
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt15(self):
         """Test Sta1 + Evt15."""
         # Sta1 + Evt15 -> <ignore> -> Sta1
@@ -668,9 +728,12 @@ class TestState01(TestStateBase):
         self.assoc._mode = "acceptor"
         self.assoc.start()
 
+        time.sleep(0.1)
+
         self.assoc.dul.send_pdu(self.get_abort(False))
 
-        time.sleep(0.5)
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         self.assoc.kill()
 
@@ -679,6 +742,7 @@ class TestState01(TestStateBase):
         assert self.fsm._events[0] == "Evt15"
         assert self.fsm.current_state == "Sta1"
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt16(self):
         """Test Sta1 + Evt16."""
         # Sta1 + Evt16 -> <ignore> -> Sta1
@@ -692,6 +756,10 @@ class TestState01(TestStateBase):
         self.assoc.dul.socket._is_connected = True
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -699,6 +767,7 @@ class TestState01(TestStateBase):
         assert self.fsm._changes == []
         assert self.fsm._events[:1] == ["Evt16"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt17(self):
         """Test Sta1 + Evt17."""
         # Sta1 + Evt17 -> <ignore> -> Sta1
@@ -714,22 +783,28 @@ class TestState01(TestStateBase):
         scp.step()
         scp.shutdown()
 
-        time.sleep(0.5)
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         assert self.fsm._transitions == []
         assert self.fsm._changes == []
         assert self.fsm._events[:1] == ["Evt17"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt18(self):
         """Test Sta1 + Evt18."""
         # Sta1 + Evt18 -> <ignore> -> Sta1
         # Evt18: ARTIM timer expired from <local service>
         self.assoc._mode = "acceptor"
         self.assoc.start()
+
+        time.sleep(0.1)
+
         self.assoc.dul.artim_timer.timeout = 0.05
         self.assoc.dul.artim_timer.start()
 
-        time.sleep(0.5)
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         self.assoc.kill()
 
@@ -740,6 +815,7 @@ class TestState01(TestStateBase):
         assert self.fsm._events[0] == "Evt18"
         assert self.fsm.current_state == "Sta1"
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt19(self):
         """Test Sta1 + Evt19."""
         # Sta1 + Evt19 -> <ignore> -> Sta1
@@ -753,6 +829,10 @@ class TestState01(TestStateBase):
         self.assoc.dul.socket._is_connected = True
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -761,7 +841,6 @@ class TestState01(TestStateBase):
         assert self.fsm._events[:1] == ["Evt19"]
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState02(TestStateBase):
     """Tests for State 02: Connection open, waiting for A-ASSOCIATE-RQ."""
 
@@ -769,6 +848,7 @@ class TestState02(TestStateBase):
         assoc.start()
         self.wait_on_state(assoc.dul.state_machine, "Sta2")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta2 + Evt1."""
         # Sta2 + Evt1 -> <ignore> -> Sta2
@@ -780,6 +860,9 @@ class TestState02(TestStateBase):
 
         assoc.dul.send_pdu(self.get_associate("request"))
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -881,6 +964,7 @@ class TestState02(TestStateBase):
         assert fsm._changes[:2] == [("Sta1", "Evt5", "AE-5"), ("Sta2", "Evt6", "AE-6")]
         assert fsm._events[:2] == ["Evt5", "Evt6"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta2 + Evt7."""
         # Sta2 + Evt7 -> <ignore> -> Sta2
@@ -892,6 +976,9 @@ class TestState02(TestStateBase):
         self.move_to_state(assoc, scp)
         assoc.dul.send_pdu(self.get_associate("accept"))
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -901,6 +988,7 @@ class TestState02(TestStateBase):
         ]
         assert fsm._events[:2] == ["Evt5", "Evt7"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta2 + Evt8."""
         # Sta2 + Evt8 -> <ignore> -> Sta2
@@ -912,6 +1000,9 @@ class TestState02(TestStateBase):
 
         assoc.dul.send_pdu(self.get_associate("reject"))
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -921,6 +1012,7 @@ class TestState02(TestStateBase):
         ]
         assert fsm._events[:2] == ["Evt5", "Evt8"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt09(self):
         """Test Sta2 + Evt9."""
         # Sta2 + Evt9 -> <ignore> -> Sta2
@@ -933,6 +1025,9 @@ class TestState02(TestStateBase):
 
         assoc.dul.send_pdu(self.get_pdata())
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -961,6 +1056,7 @@ class TestState02(TestStateBase):
         assert fsm._changes[:2] == [("Sta1", "Evt5", "AE-5"), ("Sta2", "Evt10", "AA-1")]
         assert fsm._events[:2] == ["Evt5", "Evt10"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta2 + Evt11."""
         # Sta2 + Evt11 -> <ignore> -> Sta2
@@ -973,6 +1069,9 @@ class TestState02(TestStateBase):
 
         assoc.dul.send_pdu(self.get_release(False))
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1020,6 +1119,7 @@ class TestState02(TestStateBase):
         assert fsm._changes[:2] == [("Sta1", "Evt5", "AE-5"), ("Sta2", "Evt13", "AA-1")]
         assert fsm._events[:2] == ["Evt5", "Evt13"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt14(self):
         """Test Sta2 + Evt14."""
         # Sta2 + Evt14 -> <ignore> -> Sta2
@@ -1032,6 +1132,9 @@ class TestState02(TestStateBase):
 
         assoc.dul.send_pdu(self.get_release(True))
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1041,6 +1144,7 @@ class TestState02(TestStateBase):
         ]
         assert fsm._events[:2] == ["Evt5", "Evt14"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt15(self):
         """Test Sta2 + Evt15."""
         # Sta2 + Evt15 -> <ignore> -> Sta2
@@ -1053,6 +1157,9 @@ class TestState02(TestStateBase):
 
         assoc.dul.send_pdu(self.get_abort())
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1140,7 +1247,6 @@ class TestState02(TestStateBase):
         assert fsm._events[:2] == ["Evt5", "Evt19"]
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState03(TestStateBase):
     """Tests for State 03: Awaiting A-ASSOCIATE (rsp) primitive."""
 
@@ -1149,6 +1255,7 @@ class TestState03(TestStateBase):
         scp.step()
         self.wait_on_state(assoc.dul.state_machine, "Sta3")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta3 + Evt1."""
         # Sta3 + Evt1 -> <ignore> -> Sta3
@@ -1167,6 +1274,9 @@ class TestState03(TestStateBase):
 
         assoc.dul.send_pdu(self.get_associate("request"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1330,6 +1440,7 @@ class TestState03(TestStateBase):
 
         assoc.acse._negotiate_as_acceptor = _neg_as_acc
         self.move_to_state(assoc, scp)
+        self.wait_on_state(assoc.dul.state_machine, ["Sta13", "Sta1"])
 
         scp.step()
         scp.shutdown()
@@ -1342,6 +1453,7 @@ class TestState03(TestStateBase):
         ]
         assert fsm._events[:3] == ["Evt5", "Evt6", "Evt8"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt09(self):
         """Test Sta3 + Evt9."""
         # Sta3 + Evt9 -> <ignore> -> Sta3
@@ -1360,6 +1472,9 @@ class TestState03(TestStateBase):
         assoc.acse._negotiate_as_acceptor = _neg_as_acc
         self.move_to_state(assoc, scp)
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1403,6 +1518,7 @@ class TestState03(TestStateBase):
         ]
         assert fsm._events[:3] == ["Evt5", "Evt6", "Evt10"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta3 + Evt11."""
         # Sta3 + Evt11 -> <ignore> -> Sta3
@@ -1421,6 +1537,9 @@ class TestState03(TestStateBase):
         assoc.acse._negotiate_as_acceptor = _neg_as_acc
         self.move_to_state(assoc, scp)
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1497,6 +1616,7 @@ class TestState03(TestStateBase):
         ]
         assert fsm._events[:3] == ["Evt5", "Evt6", "Evt13"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt14(self):
         """Test Sta3 + Evt14."""
         # Sta3 + Evt14 -> <ignore> -> Sta3
@@ -1515,6 +1635,9 @@ class TestState03(TestStateBase):
         assoc.acse._negotiate_as_acceptor = _neg_as_acc
         self.move_to_state(assoc, scp)
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1622,6 +1745,7 @@ class TestState03(TestStateBase):
         ]
         assert fsm._events[:3] == ["Evt5", "Evt6", "Evt17"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt18(self):
         """Test Sta3 + Evt18."""
         # Sta3 + Evt18 -> <ignore> -> Sta3
@@ -1645,6 +1769,9 @@ class TestState03(TestStateBase):
         assoc.acse._negotiate_as_acceptor = _neg_as_acc
         self.move_to_state(assoc, scp)
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.shutdown()
 
         assert fsm._transitions[:2] == ["Sta2", "Sta3"]
@@ -1692,12 +1819,12 @@ class TestState03(TestStateBase):
         assert fsm._events[:3] == ["Evt5", "Evt6", "Evt19"]
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState04(TestStateBase):
     """Tests for State 04: Awaiting TRANSPORT_OPEN from <transport service>."""
 
     def move_to_state(self, assoc, scp):
-        def connect(address):
+        def connect(primitive):
+            address = primitive.address
             """Override the socket's connect so no event gets added."""
             if assoc.dul.socket.socket is None:
                 assoc.dul.socket.socket = assoc.dul.socket._create_socket()
@@ -1713,6 +1840,7 @@ class TestState04(TestStateBase):
         assoc.start()
         self.wait_on_state(assoc.dul.state_machine, "Sta4")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta4 + Evt1."""
         # Sta4 + Evt1 -> <ignore> -> Sta4
@@ -1723,6 +1851,9 @@ class TestState04(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("request"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1739,6 +1870,7 @@ class TestState04(TestStateBase):
         # Evt2: Receive TRANSPORT_OPEN from <transport service>
         pass
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt03(self):
         """Test Sta4 + Evt3."""
         # Sta4 + Evt3 -> <ignore> -> Sta4
@@ -1748,6 +1880,10 @@ class TestState04(TestStateBase):
         self.move_to_state(self.assoc, scp)
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1757,6 +1893,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt3"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt04(self):
         """Test Sta4 + Evt4."""
         # Sta4 + Evt4 -> <ignore> -> Sta4
@@ -1766,6 +1903,10 @@ class TestState04(TestStateBase):
         self.move_to_state(self.assoc, scp)
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1784,6 +1925,7 @@ class TestState04(TestStateBase):
         #       Start ARTIM timer
         pass
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt06(self):
         """Test Sta4 + Evt6."""
         # Sta4 + Evt6 -> <ignore> -> Sta4
@@ -1793,6 +1935,10 @@ class TestState04(TestStateBase):
         self.move_to_state(self.assoc, scp)
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1802,6 +1948,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt6"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta4 + Evt7."""
         # Sta4 + Evt7 -> <ignore> -> Sta4
@@ -1812,6 +1959,9 @@ class TestState04(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("accept"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1821,6 +1971,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt7"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta4 + Evt8."""
         # Sta4 + Evt8 -> <ignore> -> Sta4
@@ -1831,6 +1982,9 @@ class TestState04(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("reject"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1840,6 +1994,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt8"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt09(self):
         """Test Sta4 + Evt9."""
         # Sta4 + Evt9 -> <ignore> -> Sta4
@@ -1850,6 +2005,9 @@ class TestState04(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_pdata())
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1859,6 +2017,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt9"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt10(self):
         """Test Sta4 + Evt10."""
         # Sta4 + Evt10 -> <ignore> -> Sta4
@@ -1868,6 +2027,10 @@ class TestState04(TestStateBase):
         self.move_to_state(self.assoc, scp)
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1877,6 +2040,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt10"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta4 + Evt11."""
         # Sta4 + Evt11 -> <ignore> -> Sta4
@@ -1887,6 +2051,9 @@ class TestState04(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(False))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1896,6 +2063,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt11"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt12(self):
         """Test Sta4 + Evt12."""
         # Sta4 + Evt12 -> <ignore> -> Sta4
@@ -1905,6 +2073,10 @@ class TestState04(TestStateBase):
         self.move_to_state(self.assoc, scp)
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1914,6 +2086,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt12"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt13(self):
         """Test Sta4 + Evt13."""
         # Sta4 + Evt13 -> <ignore> -> Sta4
@@ -1923,6 +2096,10 @@ class TestState04(TestStateBase):
         self.move_to_state(self.assoc, scp)
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1932,6 +2109,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt13"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt14(self):
         """Test Sta4 + Evt14."""
         # Sta4 + Evt14 -> <ignore> -> Sta4
@@ -1942,6 +2120,9 @@ class TestState04(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(True))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1951,6 +2132,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt14"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt15(self):
         """Test Sta4 + Evt15."""
         # Sta4 + Evt15 -> <ignore> -> Sta4
@@ -1961,6 +2143,9 @@ class TestState04(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_abort())
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1970,6 +2155,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt15"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt16(self):
         """Test Sta4 + Evt16."""
         # Sta4 + Evt16 -> <ignore> -> Sta4
@@ -1979,6 +2165,10 @@ class TestState04(TestStateBase):
         self.move_to_state(self.assoc, scp)
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -1988,6 +2178,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt16"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt17(self):
         """Test Sta4 + Evt17."""
         # Sta4 + Evt17 -> <ignore> -> Sta4
@@ -1999,7 +2190,8 @@ class TestState04(TestStateBase):
         scp.step()
         scp.shutdown()
 
-        time.sleep(0.5)
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         assert self.fsm._transitions[:1] == ["Sta4"]
         assert self.fsm._changes[:1] == [
@@ -2007,6 +2199,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt17"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt18(self):
         """Test Sta4 + Evt18."""
         # Sta4 + Evt18 -> <ignore> -> Sta4
@@ -2017,7 +2210,9 @@ class TestState04(TestStateBase):
 
         self.assoc.dul.artim_timer.timeout = 0.05
         self.assoc.dul.artim_timer.start()
-        time.sleep(0.5)
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         scp.step()
         scp.shutdown()
@@ -2028,6 +2223,7 @@ class TestState04(TestStateBase):
         ]
         assert self.fsm._events[:2] == ["Evt1", "Evt18"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt19(self):
         """Test Sta4 + Evt19."""
         # Sta4 + Evt19 -> <ignore> -> Sta4
@@ -2037,6 +2233,10 @@ class TestState04(TestStateBase):
         self.move_to_state(self.assoc, scp)
 
         scp.step()
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -2047,7 +2247,6 @@ class TestState04(TestStateBase):
         assert self.fsm._events[:2] == ["Evt1", "Evt19"]
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState05(TestStateBase):
     """Tests for State 05: Awaiting A-ASSOCIATE-AC or A-ASSOCIATE-RJ PDU."""
 
@@ -2056,6 +2255,7 @@ class TestState05(TestStateBase):
         scp.step()
         self.wait_on_state(assoc.dul.state_machine, "Sta5")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta5 + Evt1."""
         # Sta5 + Evt1 -> <ignore> -> Sta5
@@ -2066,6 +2266,9 @@ class TestState05(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("request"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -2164,6 +2367,7 @@ class TestState05(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta5 + Evt7."""
         # Sta5 + Evt7 -> <ignore> -> Sta5
@@ -2174,6 +2378,9 @@ class TestState05(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("accept"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -2184,6 +2391,7 @@ class TestState05(TestStateBase):
         assert self.fsm._transitions[:2] == ["Sta4", "Sta5"]
         assert self.fsm._events[:3] == ["Evt1", "Evt2", "Evt7"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta5 + Evt8."""
         # Sta5 + Evt8 -> <ignore> -> Sta5
@@ -2194,6 +2402,9 @@ class TestState05(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("reject"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -2204,6 +2415,7 @@ class TestState05(TestStateBase):
         assert self.fsm._transitions[:2] == ["Sta4", "Sta5"]
         assert self.fsm._events[:3] == ["Evt1", "Evt2", "Evt8"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt09(self):
         """Test Sta5 + Evt9."""
         # Sta5 + Evt9 -> <ignore> -> Sta5
@@ -2214,6 +2426,9 @@ class TestState05(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_pdata())
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -2251,6 +2466,7 @@ class TestState05(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta5 + Evt11."""
         # Sta5 + Evt11 -> <ignore> -> Sta5
@@ -2261,6 +2477,9 @@ class TestState05(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(False))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -2333,6 +2552,7 @@ class TestState05(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt14(self):
         """Test Sta5 + Evt14."""
         # Sta5 + Evt14 -> <ignore> -> Sta5
@@ -2343,6 +2563,9 @@ class TestState05(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(True))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -2427,6 +2650,7 @@ class TestState05(TestStateBase):
         assert self.fsm._transitions[:3] == ["Sta4", "Sta5", "Sta1"]
         assert self.fsm._events[:3] == ["Evt1", "Evt2", "Evt17"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt18(self):
         """Test Sta5 + Evt18."""
         # Sta5 + Evt18 -> <ignore> -> Sta5
@@ -2437,7 +2661,9 @@ class TestState05(TestStateBase):
 
         self.assoc.dul.artim_timer.timeout = 0.05
         self.assoc.dul.artim_timer.start()
-        time.sleep(0.5)
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         scp.step()
         scp.shutdown()
@@ -2481,7 +2707,6 @@ class TestState05(TestStateBase):
         )
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState06(TestStateBase):
     """Tests for State 06: Association established and ready for data."""
 
@@ -2491,6 +2716,7 @@ class TestState06(TestStateBase):
         scp.step()
         self.wait_on_state(assoc.dul.state_machine, "Sta6")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta6 + Evt1."""
         # Sta6 + Evt1 -> <ignore> -> Sta6
@@ -2501,6 +2727,9 @@ class TestState06(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("request"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -2628,6 +2857,7 @@ class TestState06(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta6 + Evt7."""
         # Sta6 + Evt7 -> <ignore> -> Sta6
@@ -2638,6 +2868,9 @@ class TestState06(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("accept"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -2649,6 +2882,7 @@ class TestState06(TestStateBase):
         assert self.fsm._transitions[:3] == ["Sta4", "Sta5", "Sta6"]
         assert self.fsm._events[:4] == ["Evt1", "Evt2", "Evt3", "Evt7"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta6 + Evt8."""
         # Sta6 + Evt8 -> <ignore> -> Sta6
@@ -2659,6 +2893,9 @@ class TestState06(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("reject"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -2802,6 +3039,7 @@ class TestState06(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt14(self):
         """Test Sta6 + Evt14."""
         # Sta6 + Evt14 -> <ignore> -> Sta6
@@ -2812,6 +3050,9 @@ class TestState06(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(True))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -2907,6 +3148,7 @@ class TestState06(TestStateBase):
         assert self.fsm._transitions[:4] == ["Sta4", "Sta5", "Sta6", "Sta1"]
         assert self.fsm._events[:4] == ["Evt1", "Evt2", "Evt3", "Evt17"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt18(self):
         """Test Sta6 + Evt18."""
         # Sta6 + Evt18 -> <ignore> -> Sta6
@@ -2917,7 +3159,9 @@ class TestState06(TestStateBase):
 
         self.assoc.dul.artim_timer.timeout = 0.05
         self.assoc.dul.artim_timer.start()
-        time.sleep(0.5)
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         scp.step()
         scp.shutdown()
@@ -2965,7 +3209,6 @@ class TestState06(TestStateBase):
         )
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState07(TestStateBase):
     """Tests for State 07: Awaiting A-RELEASE-RP PDU."""
 
@@ -2978,6 +3221,7 @@ class TestState07(TestStateBase):
         scp.step()
         self.wait_on_state(assoc.dul.state_machine, "Sta7")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta7 + Evt1."""
         # Sta7 + Evt1 -> <ignore> -> Sta7
@@ -2993,6 +3237,9 @@ class TestState07(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("request"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -3127,6 +3374,7 @@ class TestState07(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta7 + Evt7."""
         # Sta7 + Evt7 -> <ignore> -> Sta7
@@ -3142,6 +3390,9 @@ class TestState07(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("accept"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -3154,6 +3405,7 @@ class TestState07(TestStateBase):
         assert self.fsm._transitions[:4] == ["Sta4", "Sta5", "Sta6", "Sta7"]
         assert self.fsm._events[:5] == ["Evt1", "Evt2", "Evt3", "Evt11", "Evt7"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta7 + Evt8."""
         # Sta7 + Evt8 -> <ignore> -> Sta7
@@ -3169,6 +3421,9 @@ class TestState07(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("reject"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -3181,6 +3436,7 @@ class TestState07(TestStateBase):
         assert self.fsm._transitions[:4] == ["Sta4", "Sta5", "Sta6", "Sta7"]
         assert self.fsm._events[:5] == ["Evt1", "Evt2", "Evt3", "Evt11", "Evt8"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt09(self):
         """Test Sta7 + Evt9."""
         # Sta7 + Evt9 -> <ignore> -> Sta7
@@ -3196,6 +3452,9 @@ class TestState07(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_pdata())
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -3239,6 +3498,7 @@ class TestState07(TestStateBase):
         assert self.fsm._transitions[:4] == ["Sta4", "Sta5", "Sta6", "Sta7"]
         assert self.fsm._events[:5] == ["Evt1", "Evt2", "Evt3", "Evt11", "Evt10"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta7 + Evt11."""
         # Sta7 + Evt11 -> <ignore> -> Sta7
@@ -3254,6 +3514,9 @@ class TestState07(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(False))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -3326,6 +3589,7 @@ class TestState07(TestStateBase):
         assert self.fsm._transitions[:4] == ["Sta4", "Sta5", "Sta6", "Sta7"]
         assert self.fsm._events[:5] == ["Evt1", "Evt2", "Evt3", "Evt11", "Evt13"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt14(self):
         """Test Sta7 + Evt14."""
         # Sta7 + Evt14 -> <ignore> -> Sta7
@@ -3341,6 +3605,9 @@ class TestState07(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(True))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -3442,6 +3709,7 @@ class TestState07(TestStateBase):
         assert self.fsm._transitions[:4] == ["Sta4", "Sta5", "Sta6", "Sta7"]
         assert self.fsm._events[:5] == ["Evt1", "Evt2", "Evt3", "Evt11", "Evt17"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt18(self):
         """Test Sta7 + Evt18."""
         # Sta7 + Evt18 -> <ignore> -> Sta7
@@ -3457,7 +3725,9 @@ class TestState07(TestStateBase):
 
         self.assoc.dul.artim_timer.timeout = 0.05
         self.assoc.dul.artim_timer.start()
-        time.sleep(0.5)
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         scp.step()
         scp.shutdown()
@@ -3508,7 +3778,6 @@ class TestState07(TestStateBase):
         )
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState08(TestStateBase):
     """Tests for State 08: Awaiting A-RELEASE (rp) primitive."""
 
@@ -3526,6 +3795,7 @@ class TestState08(TestStateBase):
         scp.step()
         self.wait_on_state(assoc.dul.state_machine, "Sta8")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta8 + Evt1."""
         # Sta8 + Evt1 -> <ignore> -> Sta8
@@ -3541,6 +3811,9 @@ class TestState08(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("request"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -3654,6 +3927,7 @@ class TestState08(TestStateBase):
         assert self.fsm._transitions[:4] == ["Sta4", "Sta5", "Sta6", "Sta8"]
         assert self.fsm._events[:5] == ["Evt1", "Evt2", "Evt3", "Evt12", "Evt6"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta8 + Evt7."""
         # Sta8 + Evt7 -> <ignore> -> Sta8
@@ -3669,6 +3943,9 @@ class TestState08(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("accept"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -3681,6 +3958,7 @@ class TestState08(TestStateBase):
         assert self.fsm._transitions[:4] == ["Sta4", "Sta5", "Sta6", "Sta8"]
         assert self.fsm._events[:5] == ["Evt1", "Evt2", "Evt3", "Evt12", "Evt7"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta8 + Evt8."""
         # Sta8 + Evt8 -> <ignore> -> Sta8
@@ -3696,6 +3974,9 @@ class TestState08(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("reject"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -3765,6 +4046,7 @@ class TestState08(TestStateBase):
         assert self.fsm._transitions[:4] == ["Sta4", "Sta5", "Sta6", "Sta8"]
         assert self.fsm._events[:5] == ["Evt1", "Evt2", "Evt3", "Evt12", "Evt10"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta8 + Evt11."""
         # Sta8 + Evt11 -> <ignore> -> Sta8
@@ -3780,6 +4062,9 @@ class TestState08(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(False))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -3967,6 +4252,7 @@ class TestState08(TestStateBase):
         assert self.fsm._transitions[:4] == ["Sta4", "Sta5", "Sta6", "Sta8"]
         assert self.fsm._events[:5] == ["Evt1", "Evt2", "Evt3", "Evt12", "Evt17"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt18(self):
         """Test Sta8 + Evt18."""
         # Sta8 + Evt18 -> <ignore> -> Sta1
@@ -3982,7 +4268,9 @@ class TestState08(TestStateBase):
 
         self.assoc.dul.artim_timer.timeout = 0.05
         self.assoc.dul.artim_timer.start()
-        time.sleep(0.5)
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         scp.step()
         scp.shutdown()
@@ -4026,7 +4314,6 @@ class TestState08(TestStateBase):
         assert self.fsm._events[:5] == ["Evt1", "Evt2", "Evt3", "Evt12", "Evt19"]
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState09(TestStateBase):
     """Tests for State 09: Release collision req - awaiting A-RELEASE (rp)."""
 
@@ -4046,6 +4333,7 @@ class TestState09(TestStateBase):
         scp.step()
         self.wait_on_state(assoc.dul.state_machine, "Sta9")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta9 + Evt1."""
         # Sta9 + Evt1 -> <ignore> -> Sta9
@@ -4062,6 +4350,9 @@ class TestState09(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("request"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -4225,6 +4516,7 @@ class TestState09(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta9 + Evt7."""
         # Sta9 + Evt7 -> <ignore> -> Sta9
@@ -4241,6 +4533,9 @@ class TestState09(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("accept"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -4261,6 +4556,7 @@ class TestState09(TestStateBase):
             "Evt7",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta9 + Evt8."""
         # Sta9 + Evt8 -> <ignore> -> Sta9
@@ -4277,6 +4573,9 @@ class TestState09(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("reject"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -4297,6 +4596,7 @@ class TestState09(TestStateBase):
             "Evt8",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt09(self):
         """Test Sta9 + Evt9."""
         # Sta9 + Evt9 -> <ignore> -> Sta9
@@ -4313,6 +4613,9 @@ class TestState09(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_pdata())
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -4376,6 +4679,7 @@ class TestState09(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta9 + Evt11."""
         # Sta9 + Evt11 -> <ignore> -> Sta9
@@ -4392,6 +4696,9 @@ class TestState09(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(False))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -4659,6 +4966,7 @@ class TestState09(TestStateBase):
             "Evt17",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt18(self):
         """Test Sta9 + Evt18."""
         # Sta9 + Evt18 -> <ignore> -> Sta9
@@ -4675,7 +4983,9 @@ class TestState09(TestStateBase):
 
         self.assoc.dul.artim_timer.timeout = 0.05
         self.assoc.dul.artim_timer.start()
-        time.sleep(0.5)
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         scp.step()
         scp.shutdown()
@@ -4741,7 +5051,6 @@ class TestState09(TestStateBase):
         )
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState10(TestStateBase):
     """Tests for State 10: Release collision acc - awaiting A-RELEASE-RP ."""
 
@@ -4761,6 +5070,7 @@ class TestState10(TestStateBase):
         scp.step()
         self.wait_on_state(assoc.dul.state_machine, "Sta10")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta10 + Evt1."""
         # Sta10 + Evt1 -> <ignore> -> Sta10
@@ -4778,6 +5088,9 @@ class TestState10(TestStateBase):
 
         assoc.dul.send_pdu(self.get_associate("request"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -4916,6 +5229,7 @@ class TestState10(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta10 + Evt7."""
         # Sta10 + Evt7 -> <ignore> -> Sta10
@@ -4933,6 +5247,9 @@ class TestState10(TestStateBase):
 
         assoc.dul.send_pdu(self.get_associate("accept"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -4946,6 +5263,7 @@ class TestState10(TestStateBase):
         ]
         assert fsm._events[:6] == ["Evt5", "Evt6", "Evt7", "Evt11", "Evt12", "Evt7"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta10 + Evt8."""
         # Sta10 + Evt8 -> <ignore> -> Sta10
@@ -4963,6 +5281,9 @@ class TestState10(TestStateBase):
 
         assoc.dul.send_pdu(self.get_associate("reject"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -4976,6 +5297,7 @@ class TestState10(TestStateBase):
         ]
         assert fsm._events[:6] == ["Evt5", "Evt6", "Evt7", "Evt11", "Evt12", "Evt8"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt09(self):
         """Test Sta10 + Evt9."""
         # Sta10 + Evt9 -> <ignore> -> Sta10
@@ -4993,6 +5315,9 @@ class TestState10(TestStateBase):
 
         assoc.dul.send_pdu(self.get_pdata())
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -5043,6 +5368,7 @@ class TestState10(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta10 + Evt11."""
         # Sta10 + Evt11 -> <ignore> -> Sta10
@@ -5060,6 +5386,9 @@ class TestState10(TestStateBase):
 
         assoc.dul.send_pdu(self.get_release(False))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -5142,6 +5471,7 @@ class TestState10(TestStateBase):
         ]
         assert fsm._events[:6] == ["Evt5", "Evt6", "Evt7", "Evt11", "Evt12", "Evt13"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt14(self):
         """Test Sta10 + Evt14."""
         # Sta10 + Evt14 -> <ignore> -> Sta10
@@ -5159,6 +5489,9 @@ class TestState10(TestStateBase):
 
         assoc.dul.send_pdu(self.get_release(True))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -5273,6 +5606,7 @@ class TestState10(TestStateBase):
         ]
         assert fsm._events[:6] == ["Evt5", "Evt6", "Evt7", "Evt11", "Evt12", "Evt17"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt18(self):
         """Test Sta10 + Evt18."""
         # Sta10 + Evt18 -> <ignore> -> Sta10
@@ -5290,7 +5624,9 @@ class TestState10(TestStateBase):
 
         assoc.dul.artim_timer.timeout = 0.05
         assoc.dul.artim_timer.start()
-        time.sleep(0.5)
+
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
 
         scp.step()
         scp.shutdown()
@@ -5343,7 +5679,6 @@ class TestState10(TestStateBase):
         )
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState11(TestStateBase):
     """Tests for State 11: Release collision req - awaiting A-RELEASE-RP PDU"""
 
@@ -5365,6 +5700,7 @@ class TestState11(TestStateBase):
         assoc.dul.send_pdu(self.get_release(True))
         self.wait_on_state(assoc.dul.state_machine, "Sta11")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta11 + Evt1."""
         # Sta11 + Evt1 -> <ignore> -> Sta11
@@ -5381,6 +5717,9 @@ class TestState11(TestStateBase):
         self.move_to_state(self.assoc, scp)
         self.assoc.dul.send_pdu(self.get_associate("request"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.step()
         scp.shutdown()
@@ -5578,6 +5917,7 @@ class TestState11(TestStateBase):
             "Evt6",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta11 + Evt7."""
         # Sta11 + Evt7 -> <ignore> -> Sta11
@@ -5595,6 +5935,9 @@ class TestState11(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("accept"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.step()
         scp.shutdown()
@@ -5625,6 +5968,7 @@ class TestState11(TestStateBase):
             "Evt7",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta11 + Evt8."""
         # Sta11 + Evt8 -> <ignore> -> Sta11
@@ -5642,6 +5986,9 @@ class TestState11(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("reject"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.step()
         scp.shutdown()
@@ -5672,6 +6019,7 @@ class TestState11(TestStateBase):
             "Evt8",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt09(self):
         """Test Sta11 + Evt9."""
         # Sta11 + Evt9 -> <ignore> -> Sta11
@@ -5689,6 +6037,9 @@ class TestState11(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_pdata())
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.step()
         scp.shutdown()
@@ -5770,6 +6121,7 @@ class TestState11(TestStateBase):
             "Evt10",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta11 + Evt11."""
         # Sta11 + Evt11 -> <ignore> -> Sta11
@@ -5787,6 +6139,9 @@ class TestState11(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(False))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.step()
         scp.shutdown()
@@ -5917,6 +6272,7 @@ class TestState11(TestStateBase):
             "Evt13",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt14(self):
         """Test Sta11 + Evt14."""
         # Sta11 + Evt14 -> <ignore> -> Sta11
@@ -5934,6 +6290,9 @@ class TestState11(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(True))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.step()
         scp.shutdown()
@@ -6113,6 +6472,7 @@ class TestState11(TestStateBase):
             "Evt17",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt18(self):
         """Test Sta11 + Evt18."""
         # Sta11 + Evt18 -> <ignore> -> Sta11
@@ -6130,7 +6490,9 @@ class TestState11(TestStateBase):
 
         self.assoc.dul.artim_timer.timeout = 0.05
         self.assoc.dul.artim_timer.start()
-        time.sleep(0.5)
+
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
 
         scp.step()
         scp.step()
@@ -6207,7 +6569,6 @@ class TestState11(TestStateBase):
         ]
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState12(TestStateBase):
     """Tests for State 12: Release collision acc - awaiting A-RELEASE (rp)"""
 
@@ -6229,6 +6590,7 @@ class TestState12(TestStateBase):
         scp.step()
         self.wait_on_state(assoc.dul.state_machine, "Sta12")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta12 + Evt1."""
         # Sta12 + Evt1 -> <ignore> -> Sta12
@@ -6246,6 +6608,9 @@ class TestState12(TestStateBase):
         self.move_to_state(assoc, scp)
         assoc.dul.send_pdu(self.get_associate("request"))
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -6451,6 +6816,7 @@ class TestState12(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta12 + Evt7."""
         # Sta12 + Evt7 -> <ignore> -> Sta12
@@ -6469,6 +6835,9 @@ class TestState12(TestStateBase):
 
         assoc.dul.send_pdu(self.get_associate("accept"))
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -6498,6 +6867,7 @@ class TestState12(TestStateBase):
             "Evt7",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta12 + Evt8."""
         # Sta12 + Evt8 -> <ignore> -> Sta12
@@ -6516,6 +6886,9 @@ class TestState12(TestStateBase):
 
         assoc.dul.send_pdu(self.get_associate("reject"))
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -6545,6 +6918,7 @@ class TestState12(TestStateBase):
             "Evt8",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt09(self):
         """Test Sta12 + Evt9."""
         # Sta12 + Evt9 -> <ignore> -> Sta12
@@ -6563,6 +6937,9 @@ class TestState12(TestStateBase):
 
         assoc.dul.send_pdu(self.get_pdata())
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -6646,6 +7023,7 @@ class TestState12(TestStateBase):
             b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x00"
         )
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta12 + Evt11."""
         # Sta12 + Evt11 -> <ignore> -> Sta12
@@ -6664,6 +7042,9 @@ class TestState12(TestStateBase):
 
         assoc.dul.send_pdu(self.get_release(False))
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -7005,6 +7386,7 @@ class TestState12(TestStateBase):
             "Evt17",
         ]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt18(self):
         """Test Sta12 + Evt18."""
         # Sta12 + Evt18 -> <ignore> -> Sta12
@@ -7023,7 +7405,9 @@ class TestState12(TestStateBase):
 
         assoc.dul.artim_timer.timeout = 0.05
         assoc.dul.artim_timer.start()
-        time.sleep(0.5)
+
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
 
         scp.step()
         scp.shutdown()
@@ -7109,7 +7493,6 @@ class TestState12(TestStateBase):
         )
 
 
-@pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning")
 class TestState13(TestStateBase):
     """Tests for State 13: Waiting for connection closed."""
 
@@ -7139,6 +7522,7 @@ class TestState13(TestStateBase):
         scp.step()
         self.wait_on_state(assoc.dul.state_machine, "Sta13")
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt01(self):
         """Test Sta13 + Evt1."""
         # Sta13 + Evt1 -> <ignore> -> Sta13
@@ -7153,6 +7537,9 @@ class TestState13(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("request"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -7273,6 +7660,7 @@ class TestState13(TestStateBase):
         assert self.fsm._transitions[:3] == ["Sta4", "Sta5", "Sta13"]
         assert self.fsm._events[:4] == ["Evt1", "Evt2", "Evt6", "Evt6"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt07(self):
         """Test Sta13 + Evt7."""
         # Sta13 + Evt7 -> <ignore> -> Sta13
@@ -7287,6 +7675,9 @@ class TestState13(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("accept"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -7298,6 +7689,7 @@ class TestState13(TestStateBase):
         assert self.fsm._transitions[:3] == ["Sta4", "Sta5", "Sta13"]
         assert self.fsm._events[:4] == ["Evt1", "Evt2", "Evt6", "Evt7"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt08(self):
         """Test Sta13 + Evt8."""
         # Sta13 + Evt8 -> <ignore> -> Sta13
@@ -7312,6 +7704,9 @@ class TestState13(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_associate("reject"))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -7323,6 +7718,7 @@ class TestState13(TestStateBase):
         assert self.fsm._transitions[:3] == ["Sta4", "Sta5", "Sta13"]
         assert self.fsm._events[:4] == ["Evt1", "Evt2", "Evt6", "Evt8"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt09(self):
         """Test Sta13 + Evt9."""
         # Sta13 + Evt9 -> <ignore> -> Sta13
@@ -7337,6 +7733,9 @@ class TestState13(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_pdata())
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -7375,6 +7774,7 @@ class TestState13(TestStateBase):
         assert self.fsm._transitions[:3] == ["Sta4", "Sta5", "Sta13"]
         assert self.fsm._events[:4] == ["Evt1", "Evt2", "Evt6", "Evt10"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt11(self):
         """Test Sta13 + Evt11."""
         # Sta13 + Evt11 -> <ignore> -> Sta13
@@ -7389,6 +7789,9 @@ class TestState13(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(False))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -7454,6 +7857,7 @@ class TestState13(TestStateBase):
         assert self.fsm._transitions[:3] == ["Sta4", "Sta5", "Sta13"]
         assert self.fsm._events[:4] == ["Evt1", "Evt2", "Evt6", "Evt13"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt14(self):
         """Test Sta13 + Evt14."""
         # Sta13 + Evt14 -> <ignore> -> Sta13
@@ -7468,6 +7872,9 @@ class TestState13(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_release(True))
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -7479,6 +7886,7 @@ class TestState13(TestStateBase):
         assert self.fsm._transitions[:3] == ["Sta4", "Sta5", "Sta13"]
         assert self.fsm._events[:4] == ["Evt1", "Evt2", "Evt6", "Evt14"]
 
+    @pytest.mark.filterwarnings(IGNORE_FILTER)
     def test_evt15(self):
         """Test Sta13 + Evt15."""
         # Sta13 + Evt15 -> <ignore> -> Sta13
@@ -7493,6 +7901,9 @@ class TestState13(TestStateBase):
 
         self.assoc.dul.send_pdu(self.get_abort())
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
         scp.step()
         scp.shutdown()
 
@@ -7646,9 +8057,13 @@ class TestParrotAttack(TestStateBase):
         self.scp = scp = self.start_server(commands)
         self.assoc.start()
 
-        for ii in range(len(commands)):
+        for ii in range(len(commands) - 1):
             scp.step()
 
+        while self.assoc.dul.is_alive():
+            time.sleep(0.001)
+
+        scp.step()
         scp.shutdown()
 
         assert self.fsm._changes[:14] == [
@@ -7721,9 +8136,13 @@ class TestParrotAttack(TestStateBase):
         assoc, fsm = self.get_acceptor_assoc()
         assoc.start()
 
-        for ii in range(len(commands)):
+        for ii in range(len(commands) - 1):
             scp.step()
 
+        while assoc.dul.is_alive():
+            time.sleep(0.001)
+
+        scp.step()
         scp.shutdown()
 
         assert [
@@ -8156,10 +8575,11 @@ class TestStateMachineFunctionalRequestor:
             dul.socket.send(p_data_tf)
 
             # Normal release response
-            dul.pdu = A_RELEASE_RP()
-            dul.pdu.from_primitive(dul.primitive)
+            primitive = dul.to_provider_queue.get(False)
+            pdu = A_RELEASE_RP()
+            pdu.from_primitive(primitive)
             # Callback
-            dul.socket.send(dul.pdu.encode())
+            dul.socket.send(pdu.encode())
             dul.artim_timer.start()
             return "Sta13"
 
@@ -8226,8 +8646,11 @@ class TestStateMachineFunctionalRequestor:
             # Send C-ECHO request to the peer via DIMSE and wait for the response
             dul.assoc.dimse.send_msg(primitive, 1)
 
+            pdu = dul._recv_pdu.get(False)
+            primitive = pdu.to_primitive()
+
             # Normal AR2 response
-            dul.to_user_queue.put(dul.primitive)
+            dul.to_user_queue.put(primitive)
             return "Sta8"
 
         # In this case the association acceptor will hit AR_2
@@ -8361,10 +8784,11 @@ class TestStateMachineFunctionalAcceptor:
         assert self.fsm.current_state == "Sta1"
 
         def AE_2(dul):
-            dul.pdu = A_ASSOCIATE_RQ()
-            dul.pdu.from_primitive(dul.primitive)
-            dul.pdu.protocol_version = 0x0002
-            bytestream = dul.pdu.encode()
+            conn = dul.to_provider_queue.get(False)
+            pdu = A_ASSOCIATE_RQ()
+            pdu.from_primitive(conn.request)
+            pdu.protocol_version = 0x0002
+            bytestream = pdu.encode()
             dul.socket.send(bytestream)
             return "Sta5"
 
diff --git a/pynetdicom/tests/test_transport.py b/pynetdicom/tests/test_transport.py
index e32f44277..04369150c 100644
--- a/pynetdicom/tests/test_transport.py
+++ b/pynetdicom/tests/test_transport.py
@@ -21,11 +21,13 @@ from pynetdicom import AE, evt, _config, debug_logger
 from pynetdicom.association import Association
 from pynetdicom.events import Event
 from pynetdicom._globals import MODE_REQUESTOR
+from pynetdicom.pdu_primitives import A_ASSOCIATE
 from pynetdicom import transport
 from pynetdicom.transport import (
     AssociationSocket,
     AssociationServer,
     ThreadedAssociationServer,
+    T_CONNECT,
 )
 from pynetdicom.sop_class import Verification, RTImageStorage
 from .encoded_pdu_items import p_data_tf_rq
@@ -54,6 +56,35 @@ DATASET = dcmread(os.path.join(DCM_DIR, "RTImageStorage.dcm"))
 # debug_logger()
 
 
+class TestTConnect:
+    """Tests for T_CONNECT."""
+
+    def test_bad_addr_raises(self):
+        """Test a bad init parameter raises exception"""
+        msg = (
+            r"'address' must be 'Tuple\[str, int\]' or "
+            r"'pynetdicom.pdu_primitives.A_ASSOCIATE', not 'NoneType'"
+        )
+        with pytest.raises(TypeError, match=msg):
+            T_CONNECT(None)
+
+    def test_address_tuple(self):
+        """Test init with a tuple"""
+        conn = T_CONNECT(("123", 12))
+        assert conn.address == ("123", 12)
+        assert conn.request is None
+        assert conn.result == ""
+
+    def test_address_request(self):
+        """Test init with an A-ASSOCIATE primitive"""
+        request = A_ASSOCIATE()
+        request.called_presentation_address = ("123", 12)
+        conn = T_CONNECT(request)
+        assert conn.address == ("123", 12)
+        assert conn.request is request
+        assert conn.result == ""
+
+
 class TestAssociationSocket:
     """Tests for the transport.AssociationSocket class."""
 
@@ -128,7 +159,7 @@ class TestAssociationSocket:
         sock.close()
         assert sock.socket is None
         # Tries to connect, sets to None if fails
-        sock.connect(("", 11112))
+        sock.connect(T_CONNECT(("", 11112)))
         assert sock.event_queue.get() == "Evt17"
         assert sock.socket is None