def sendPdu(self, snmpEngine, transportDomain, transportAddress,
                messageProcessingModel, securityModel, securityName,
                securityLevel, contextEngineId, contextName,
                pduVersion, PDU, expectResponse, timeout=0,
                cbFun=None, cbCtx=None):
        """PDU dispatcher -- prepare and serialize a request or notification"""
        # 4.1.1.2
        k = int(messageProcessingModel)

        if k in snmpEngine.messageProcessingSubsystems:
            mpHandler = snmpEngine.messageProcessingSubsystems[k]

        else:
            raise error.StatusInformation(
                errorIndication=errind.unsupportedMsgProcessingModel)

        debug.logger & debug.FLAG_DSP and debug.logger(
            'sendPdu: securityName %s, PDU\n'
            '%s' % (securityName, PDU.prettyPrint()))

        # 4.1.1.3
        sendPduHandle = self._sendPduHandle()

        if expectResponse:
            self._cache.add(
                sendPduHandle,
                messageProcessingModel=messageProcessingModel,
                sendPduHandle=sendPduHandle,
                timeout=timeout + snmpEngine.transportDispatcher.getTimerTicks(),
                cbFun=cbFun,
                cbCtx=cbCtx
            )

            debug.logger & debug.FLAG_DSP and debug.logger(
                'sendPdu: current time %d ticks, one tick is %s '
                'seconds' % (snmpEngine.transportDispatcher.getTimerTicks(),
                             snmpEngine.transportDispatcher.getTimerResolution()))

        debug.logger & debug.FLAG_DSP and debug.logger(
            'sendPdu: new sendPduHandle %s, timeout %s ticks, cbFun '
            '%s' % (sendPduHandle, timeout, cbFun))

        origTransportDomain = transportDomain
        origTransportAddress = transportAddress

        # 4.1.1.4 & 4.1.1.5
        try:
            (transportDomain,
             transportAddress,
             outgoingMessage) = mpHandler.prepareOutgoingMessage(
                snmpEngine, origTransportDomain, origTransportAddress,
                messageProcessingModel, securityModel, securityName,
                securityLevel, contextEngineId, contextName,
                pduVersion, PDU, expectResponse, sendPduHandle
            )

            debug.logger & debug.FLAG_DSP and debug.logger(
                'sendPdu: MP succeeded')

        except PySnmpError:
            if expectResponse:
                self._cache.pop(sendPduHandle)
                self.releaseStateInformation(snmpEngine, sendPduHandle, messageProcessingModel)

            raise

        # 4.1.1.6
        if snmpEngine.transportDispatcher is None:
            if expectResponse:
                self._cache.pop(sendPduHandle)

            raise error.PySnmpError('Transport dispatcher not set')

        snmpEngine.observer.storeExecutionContext(
            snmpEngine, 'rfc3412.sendPdu',
            dict(transportDomain=transportDomain,
                 transportAddress=transportAddress,
                 outgoingMessage=outgoingMessage,
                 messageProcessingModel=messageProcessingModel,
                 securityModel=securityModel,
                 securityName=securityName,
                 securityLevel=securityLevel,
                 contextEngineId=contextEngineId,
                 contextName=contextName,
                 pdu=PDU)
        )

        try:
            snmpEngine.transportDispatcher.sendMessage(
                outgoingMessage, transportDomain, transportAddress)

        except PySnmpError:
            if expectResponse:
                self._cache.pop(sendPduHandle)
            raise

        snmpEngine.observer.clearExecutionContext(
            snmpEngine, 'rfc3412.sendPdu')

        # Update cache with orignal req params (used for retrying)
        if expectResponse:
            self._cache.update(
                sendPduHandle,
                transportDomain=origTransportDomain,
                transportAddress=origTransportAddress,
                securityModel=securityModel,
                securityName=securityName,
                securityLevel=securityLevel,
                contextEngineId=contextEngineId,
                contextName=contextName,
                pduVersion=pduVersion,
                PDU=PDU
            )

        return sendPduHandle