+static inline int
+app_enqueue_evt (svm_msg_q_t * mq, svm_msg_q_msg_t * msg, u8 lock)
+{
+ if (PREDICT_FALSE (svm_msg_q_is_full (mq)))
+ {
+ clib_warning ("evt q full");
+ svm_msg_q_free_msg (mq, msg);
+ if (lock)
+ svm_msg_q_unlock (mq);
+ return -1;
+ }
+
+ if (lock)
+ {
+ svm_msg_q_add_and_unlock (mq, msg);
+ return 0;
+ }
+
+ /* Even when not locking the ring, we must wait for queue mutex */
+ if (svm_msg_q_add (mq, msg, SVM_Q_WAIT))
+ {
+ clib_warning ("msg q add returned");
+ return -1;
+ }
+ return 0;
+}
+
+static inline int
+app_send_io_evt_rx (app_worker_t * app_wrk, stream_session_t * s, u8 lock)
+{
+ session_event_t *evt;
+ svm_msg_q_msg_t msg;
+ svm_msg_q_t *mq;
+
+ if (PREDICT_FALSE (s->session_state != SESSION_STATE_READY
+ && s->session_state != SESSION_STATE_LISTENING))
+ {
+ /* Session is closed so app will never clean up. Flush rx fifo */
+ if (s->session_state == SESSION_STATE_CLOSED)
+ svm_fifo_dequeue_drop_all (s->server_rx_fifo);
+ return 0;
+ }
+
+ if (app_worker_application_is_builtin (app_wrk))
+ {
+ application_t *app = application_get (app_wrk->app_index);
+ return app->cb_fns.builtin_app_rx_callback (s);
+ }
+
+ if (svm_fifo_has_event (s->server_rx_fifo)
+ || svm_fifo_is_empty (s->server_rx_fifo))
+ return 0;
+
+ mq = app_wrk->event_queue;
+ if (lock)
+ svm_msg_q_lock (mq);
+
+ if (PREDICT_FALSE (svm_msg_q_ring_is_full (mq, SESSION_MQ_IO_EVT_RING)))
+ {
+ clib_warning ("evt q rings full");
+ if (lock)
+ svm_msg_q_unlock (mq);
+ return -1;
+ }
+
+ msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
+ ASSERT (!svm_msg_q_msg_is_invalid (&msg));
+
+ evt = (session_event_t *) svm_msg_q_msg_data (mq, &msg);
+ evt->fifo = s->server_rx_fifo;
+ evt->event_type = FIFO_EVENT_APP_RX;
+
+ if (app_enqueue_evt (mq, &msg, lock))
+ return -1;
+ (void) svm_fifo_set_event (s->server_rx_fifo);
+ return 0;
+}
+
+static inline int
+app_send_io_evt_tx (app_worker_t * app_wrk, stream_session_t * s, u8 lock)
+{
+ svm_msg_q_t *mq;
+ session_event_t *evt;
+ svm_msg_q_msg_t msg;
+
+ if (app_worker_application_is_builtin (app_wrk))
+ return 0;
+
+ mq = app_wrk->event_queue;
+ if (lock)
+ svm_msg_q_lock (mq);
+
+ if (PREDICT_FALSE (svm_msg_q_ring_is_full (mq, SESSION_MQ_IO_EVT_RING)))
+ {
+ clib_warning ("evt q rings full");
+ if (lock)
+ svm_msg_q_unlock (mq);
+ return -1;
+ }
+
+ msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
+ ASSERT (!svm_msg_q_msg_is_invalid (&msg));
+
+ evt = (session_event_t *) svm_msg_q_msg_data (mq, &msg);
+ evt->event_type = FIFO_EVENT_APP_TX;
+ evt->fifo = s->server_tx_fifo;
+
+ return app_enqueue_evt (mq, &msg, lock);
+}
+
+/* *INDENT-OFF* */
+typedef int (app_send_evt_handler_fn) (app_worker_t *app,
+ stream_session_t *s,
+ u8 lock);
+static app_send_evt_handler_fn * const app_send_evt_handler_fns[3] = {
+ app_send_io_evt_rx,
+ 0,
+ app_send_io_evt_tx,
+};
+/* *INDENT-ON* */
+
+/**
+ * Send event to application
+ *
+ * Logic from queue perspective is non-blocking. If there's
+ * not enough space to enqueue a message, we return.
+ */
+int
+app_worker_send_event (app_worker_t * app, stream_session_t * s, u8 evt_type)
+{
+ ASSERT (app && evt_type <= FIFO_EVENT_APP_TX);
+ return app_send_evt_handler_fns[evt_type] (app, s, 0 /* lock */ );
+}
+
+/**
+ * Send event to application
+ *
+ * Logic from queue perspective is blocking. However, if queue is full,
+ * we return.
+ */
+int
+app_worker_lock_and_send_event (app_worker_t * app, stream_session_t * s,
+ u8 evt_type)
+{
+ return app_send_evt_handler_fns[evt_type] (app, s, 1 /* lock */ );
+}
+