/* map of services IDs are run on this core */
        uint64_t service_mask;
        uint8_t runstate; /* running or stopped */
+       uint8_t thread_active; /* indicates when thread is in service_run() */
        uint8_t is_service_core; /* set if core is currently a service core */
        uint8_t service_active_on_lcore[RTE_SERVICE_NUM_MAX];
        uint64_t loops;
        const int lcore = rte_lcore_id();
        struct core_state *cs = &lcore_states[lcore];
 
+       __atomic_store_n(&cs->thread_active, 1, __ATOMIC_SEQ_CST);
+
        /* runstate act as the guard variable. Use load-acquire
         * memory order here to synchronize with store-release
         * in runstate update functions.
                cs->loops++;
        }
 
+       /* Use SEQ CST memory ordering to avoid any re-ordering around
+        * this store, ensuring that once this store is visible, the service
+        * lcore thread really is done in service cores code.
+        */
+       __atomic_store_n(&cs->thread_active, 0, __ATOMIC_SEQ_CST);
        return 0;
 }
 
+int32_t
+rte_service_lcore_may_be_active(uint32_t lcore)
+{
+       if (lcore >= RTE_MAX_LCORE || !lcore_states[lcore].is_service_core)
+               return -EINVAL;
+
+       /* Load thread_active using ACQUIRE to avoid instructions dependent on
+        * the result being re-ordered before this load completes.
+        */
+       return __atomic_load_n(&lcore_states[lcore].thread_active,
+                              __ATOMIC_ACQUIRE);
+}
+
 int32_t
 rte_service_lcore_count(void)
 {
 
  * Stop a service core.
  *
  * Stopping a core makes the core become idle, but remains  assigned as a
- * service core.
+ * service core. Note that the service lcore thread may not have returned from
+ * the service it is running when this API returns.
+ *
+ * The *rte_service_lcore_may_be_active* API can be used to check if the
+ * service lcore is * still active.
  *
  * @retval 0 Success
  * @retval -EINVAL Invalid *lcore_id* provided
  */
 int32_t rte_service_lcore_stop(uint32_t lcore_id);
 
+/**
+ * Reports if a service lcore is currently running.
+ *
+ * This function returns if the core has finished service cores code, and has
+ * returned to EAL control. If *rte_service_lcore_stop* has been called but
+ * the lcore has not returned to EAL yet, it might be required to wait and call
+ * this function again. The amount of time to wait before the core returns
+ * depends on the duration of the services being run.
+ *
+ * @retval 0 Service thread is not active, and lcore has been returned to EAL.
+ * @retval 1 Service thread is in the service core polling loop.
+ * @retval -EINVAL Invalid *lcore_id* provided.
+ */
+__rte_experimental
+int32_t rte_service_lcore_may_be_active(uint32_t lcore_id);
+
 /**
  * Adds lcore to the list of service cores.
  *