Repository: nasa/fprime (language: python)

Issue:
StubFileTest Randomly Fails
| | |
|:---|:---|
|**_F´ Version_**| |
|**_Affected Component_**|   |
---
## Problem Description

The `StubFileTest` randomly fails.


Discussion / hints:

Fix patch:
diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt
index ba1456bcca..fc305cc9f3 100644
--- a/.github/actions/spelling/expect.txt
+++ b/.github/actions/spelling/expect.txt
@@ -294,6 +294,7 @@ ect
 edu
 EGB
 EHAs
+eip
 elist
 ELOG
 Elts
@@ -1012,6 +1013,7 @@ svipc
 swcaegitadmin
 synchronicity
 synopsys
+sysconf
 SYSCONFDIR
 SYSFS
 sysinfo
@@ -1110,6 +1112,7 @@ toolset
 topologyapp
 Torvalds
 totalram
+tparam
 treeview
 trimwhitespace
 trinomials
diff --git a/Drv/Ip/SocketReadTask.cpp b/Drv/Ip/SocketReadTask.cpp
index 58b414aa33..da9f34c01c 100644
--- a/Drv/Ip/SocketReadTask.cpp
+++ b/Drv/Ip/SocketReadTask.cpp
@@ -23,17 +23,18 @@ SocketReadTask::SocketReadTask() : m_reconnect(false), m_stop(false) {}
 
 SocketReadTask::~SocketReadTask() {}
 
-void SocketReadTask::startSocketTask(const Fw::StringBase &name,
+void SocketReadTask::start(const Fw::StringBase &name,
                                      const bool reconnect,
                                      const Os::Task::ParamType priority,
                                      const Os::Task::ParamType stack,
                                      const Os::Task::ParamType cpuAffinity) {
-    FW_ASSERT(not m_task.isStarted());  // It is a coding error to start this task multiple times
+    FW_ASSERT(m_task.getState() == Os::Task::State::NOT_STARTED);  // It is a coding error to start this task multiple times
     FW_ASSERT(not this->m_stop);        // It is a coding error to stop the thread before it is started
     m_reconnect = reconnect;
     // Note: the first step is for the IP socket to open the port
-    Os::Task::TaskStatus stat = m_task.start(name, SocketReadTask::readTask, this, priority, stack, cpuAffinity);
-    FW_ASSERT(Os::Task::TASK_OK == stat, static_cast<NATIVE_INT_TYPE>(stat));
+    Os::Task::Arguments arguments(name, SocketReadTask::readTask, this, priority, stack, cpuAffinity);
+    Os::Task::Status stat = m_task.start(arguments);
+    FW_ASSERT(Os::Task::OP_OK == stat, static_cast<NATIVE_INT_TYPE>(stat));
 }
 
 SocketIpStatus SocketReadTask::startup() {
@@ -57,11 +58,11 @@ void SocketReadTask::close() {
     this->getSocketHandler().close();
 }
 
-Os::Task::TaskStatus SocketReadTask::joinSocketTask(void** value_ptr) {
-    return m_task.join(value_ptr);
+Os::Task::Status SocketReadTask::join() {
+    return m_task.join();
 }
 
-void SocketReadTask::stopSocketTask() {
+void SocketReadTask::stop() {
     this->m_stop = true;
     this->getSocketHandler().shutdown();  // Break out of any receives and fully shutdown
 }
@@ -78,7 +79,7 @@ void SocketReadTask::readTask(void* pointer) {
                 "[WARNING] Failed to open port with status %d and errno %d\n",
                 static_cast<POINTER_CAST>(status),
                 static_cast<POINTER_CAST>(errno));
-            (void) Os::Task::delay(SOCKET_RETRY_INTERVAL_MS);
+            (void) Os::Task::delay(SOCKET_RETRY_INTERVAL);
             continue;
         }
 
@@ -89,7 +90,7 @@ void SocketReadTask::readTask(void* pointer) {
                 "[WARNING] Failed to open port with status %d and errno %d\n",
                 static_cast<POINTER_CAST>(status),
                 static_cast<POINTER_CAST>(errno));
-            (void) Os::Task::delay(SOCKET_RETRY_INTERVAL_MS);
+            (void) Os::Task::delay(SOCKET_RETRY_INTERVAL);
             continue;
         }
 
diff --git a/Drv/Ip/SocketReadTask.hpp b/Drv/Ip/SocketReadTask.hpp
index dd3741d25e..5958782ae3 100644
--- a/Drv/Ip/SocketReadTask.hpp
+++ b/Drv/Ip/SocketReadTask.hpp
@@ -49,7 +49,7 @@ class SocketReadTask {
      * \param stack: stack size provided to the task. See: Os::Task::start. Default: TASK_DEFAULT, posix threads default
      * \param cpuAffinity: cpu affinity provided to task. See: Os::Task::start. Default: TASK_DEFAULT, don't care
      */
-    void startSocketTask(const Fw::StringBase &name,
+    void start(const Fw::StringBase &name,
                          const bool reconnect = true,
                          const Os::Task::ParamType priority = Os::Task::TASK_DEFAULT,
                          const Os::Task::ParamType stack = Os::Task::TASK_DEFAULT,
@@ -106,7 +106,7 @@ class SocketReadTask {
      * Called to stop the socket read task. It is an error to call this before the thread has been started using the
      * startSocketTask call. This will stop the read task and close the client socket.
      */
-    void stopSocketTask();
+    void stop();
 
     /**
      * \brief joins to the stopping read task to wait for it to close
@@ -114,9 +114,9 @@ class SocketReadTask {
      * Called to join with the read socket task. This will block and return after the task has been stopped with a call
      * to the stopSocketTask method.
      * \param value_ptr: a pointer to fill with data. Passed to the Os::Task::join call. NULL to ignore.
-     * \return: Os::Task::TaskStatus passed back from the Os::Task::join call.
+     * \return: Os::Task::Status passed back from the Os::Task::join call.
      */
-    Os::Task::TaskStatus joinSocketTask(void** value_ptr);
+    Os::Task::Status join();
 
 
   PROTECTED:
diff --git a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.cpp b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.cpp
index 7af221c06e..ddbae28b26 100644
--- a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.cpp
+++ b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.cpp
@@ -396,13 +396,14 @@ namespace Drv {
 
   }
 
-  Os::Task::TaskStatus LinuxGpioDriverComponentImpl ::
-  startIntTask(NATIVE_UINT_TYPE priority, NATIVE_UINT_TYPE stackSize, NATIVE_UINT_TYPE cpuAffinity) {
+  Os::Task::Status LinuxGpioDriverComponentImpl ::
+  startIntTask(Os::Task::ParamType priority, Os::Task::ParamType stackSize, Os::Task::ParamType cpuAffinity) {
       Os::TaskString name;
       name.format("GPINT_%s",this->getObjName()); // The task name can only be 16 chars including null
-      Os::Task::TaskStatus stat = this->m_intTask.start(name, LinuxGpioDriverComponentImpl::intTaskEntry, this, priority, stackSize, cpuAffinity);
+      Os::Task::Arguments arguments(name, LinuxGpioDriverComponentImpl::intTaskEntry, this, priority, stackSize, cpuAffinity);
+      Os::Task::Status stat = this->m_intTask.start(arguments);
 
-      if (stat != Os::Task::TASK_OK) {
+      if (stat != Os::Task::OP_OK) {
           DEBUG_PRINT("Task start error: %d\n",stat);
       }
 
diff --git a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.hpp b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.hpp
index a0c7ecad7f..46c40318dc 100644
--- a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.hpp
+++ b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.hpp
@@ -45,9 +45,9 @@ namespace Drv {
       ~LinuxGpioDriverComponentImpl();
 
       //! Start interrupt task
-      Os::Task::TaskStatus startIntTask(NATIVE_UINT_TYPE priority = Os::Task::TASK_DEFAULT,
-                                        NATIVE_UINT_TYPE stackSize = Os::Task::TASK_DEFAULT,
-                                        NATIVE_UINT_TYPE cpuAffinity = Os::Task::TASK_DEFAULT);
+      Os::Task::Status startIntTask(Os::Task::ParamType priority = Os::Task::TASK_DEFAULT,
+                                    Os::Task::ParamType stackSize = Os::Task::TASK_DEFAULT,
+                                    Os::Task::ParamType cpuAffinity = Os::Task::TASK_DEFAULT);
 
       //! configure GPIO
       enum GpioDirection {
diff --git a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImplStub.cpp b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImplStub.cpp
index 31e9d7efe8..ebe891c93c 100644
--- a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImplStub.cpp
+++ b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImplStub.cpp
@@ -43,9 +43,9 @@ namespace Drv {
       return false;
   }
 
-  Os::Task::TaskStatus LinuxGpioDriverComponentImpl ::
-    startIntTask(NATIVE_UINT_TYPE priority, NATIVE_UINT_TYPE stackSize, NATIVE_UINT_TYPE cpuAffinity) {
-     return Os::Task::TASK_OK;
+  Os::Task::Status LinuxGpioDriverComponentImpl ::
+    startIntTask(Os::Task::ParamType priority, Os::Task::ParamType stackSize, Os::Task::ParamType cpuAffinity) {
+     return Os::Task::OP_OK;
    }
 
   LinuxGpioDriverComponentImpl ::
diff --git a/Drv/LinuxUartDriver/LinuxUartDriver.cpp b/Drv/LinuxUartDriver/LinuxUartDriver.cpp
index 2965e455e4..0524644e4b 100644
--- a/Drv/LinuxUartDriver/LinuxUartDriver.cpp
+++ b/Drv/LinuxUartDriver/LinuxUartDriver.cpp
@@ -355,7 +355,7 @@ void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
             status = RecvStatus::RECV_ERROR;
             comp->recv_out(0, buff, status);
             // to avoid spinning, wait 50 ms
-            Os::Task::delay(50);
+            Os::Task::delay(Fw::Time(0, 50));
             continue;
         }
 
@@ -389,21 +389,19 @@ void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
     }
 }
 
-void LinuxUartDriver ::startReadThread(NATIVE_UINT_TYPE priority,
-                                       NATIVE_UINT_TYPE stackSize,
-                                       NATIVE_UINT_TYPE cpuAffinity) {
+void LinuxUartDriver ::start(Os::Task::ParamType priority, Os::Task::ParamType stackSize, Os::Task::ParamType cpuAffinity) {
     Os::TaskString task("SerReader");
-    Os::Task::TaskStatus stat =
-        this->m_readTask.start(task, serialReadTaskEntry, this, priority, stackSize, cpuAffinity);
-    FW_ASSERT(stat == Os::Task::TASK_OK, stat);
+    Os::Task::Arguments arguments(task, serialReadTaskEntry, this, priority, stackSize, cpuAffinity);
+    Os::Task::Status stat = this->m_readTask.start(arguments);
+    FW_ASSERT(stat == Os::Task::OP_OK, stat);
 }
 
 void LinuxUartDriver ::quitReadThread() {
     this->m_quitReadThread = true;
 }
 
-Os::Task::TaskStatus LinuxUartDriver ::join(void** value_ptr) {
-    return m_readTask.join(value_ptr);
+Os::Task::Status LinuxUartDriver ::join() {
+    return m_readTask.join();
 }
 
 }  // end namespace Drv
diff --git a/Drv/LinuxUartDriver/LinuxUartDriver.hpp b/Drv/LinuxUartDriver/LinuxUartDriver.hpp
index d5a77b2dc0..0e6bb5adca 100644
--- a/Drv/LinuxUartDriver/LinuxUartDriver.hpp
+++ b/Drv/LinuxUartDriver/LinuxUartDriver.hpp
@@ -77,15 +77,15 @@ class LinuxUartDriver : public LinuxUartDriverComponentBase {
     //! start the serial poll thread.
     //! buffSize is the max receive buffer size
     //!
-    void startReadThread(NATIVE_UINT_TYPE priority = Os::Task::TASK_DEFAULT,
-                         NATIVE_UINT_TYPE stackSize = Os::Task::TASK_DEFAULT,
-                         NATIVE_UINT_TYPE cpuAffinity = Os::Task::TASK_DEFAULT);
+    void start(Os::Task::ParamType priority = Os::Task::TASK_DEFAULT,
+               Os::Task::ParamType stackSize = Os::Task::TASK_DEFAULT,
+               Os::Task::ParamType cpuAffinity = Os::Task::TASK_DEFAULT);
 
     //! Quit thread
     void quitReadThread();
 
     //! Join thread
-    Os::Task::TaskStatus join(void** value_ptr);
+    Os::Task::Status join();
 
     //! Destroy object LinuxUartDriver
     //!
diff --git a/Fw/Comp/ActiveComponentBase.cpp b/Fw/Comp/ActiveComponentBase.cpp
index bc153bb18a..35fafd16a0 100644
--- a/Fw/Comp/ActiveComponentBase.cpp
+++ b/Fw/Comp/ActiveComponentBase.cpp
@@ -63,15 +63,12 @@ namespace Fw {
         (void)snprintf(taskNameChar,sizeof(taskNameChar),"ActComp_%d",Os::Task::getNumTasks());
         taskName = taskNameChar;
 #endif
-// If running with the baremetal scheduler, use a variant of the task-loop that
-// does not loop internal, but waits for an external iteration call.
-#if FW_BAREMETAL_SCHEDULER == 1
-        Os::Task::taskRoutine routine = this->s_baseBareTask;
-#else
-        Os::Task::taskRoutine routine = this->s_baseTask;
-#endif
-        Os::Task::TaskStatus status = this->m_task.start(taskName, routine, this, priority, stackSize, cpuAffinity, identifier);
-        FW_ASSERT(status == Os::Task::TASK_OK,static_cast<NATIVE_INT_TYPE>(status));
+        // Cooperative threads tasks externalize the task loop, and as such use the state machine as their task function
+        // Standard multithreading tasks use the task loop to respectively call the state machine
+        Os::Task::taskRoutine routine = (m_task.isCooperative()) ? this->s_taskStateMachine : this->s_taskLoop;
+        Os::Task::Arguments arguments(taskName, routine, this, priority, stackSize, cpuAffinity, static_cast<PlatformUIntType>(identifier));
+        Os::Task::Status status = this->m_task.start(arguments);
+        FW_ASSERT(status == Os::Task::Status::OP_OK,static_cast<NATIVE_INT_TYPE>(status));
     }
 
     void ActiveComponentBase::exit() {
@@ -82,66 +79,68 @@ namespace Fw {
         DEBUG_PRINT("exit %s\n", this->getObjName());
     }
 
-    Os::Task::TaskStatus ActiveComponentBase::join(void **value_ptr) {
+    Os::Task::Status ActiveComponentBase::join() {
         DEBUG_PRINT("join %s\n", this->getObjName());
-        return this->m_task.join(value_ptr);
+        return this->m_task.join();
     }
 
-    void ActiveComponentBase::s_baseBareTask(void* ptr) {
-        FW_ASSERT(ptr != nullptr);
-        ActiveComponentBase* comp = reinterpret_cast<ActiveComponentBase*>(ptr);
-        //Start if not started
-        if (!comp->m_task.isStarted()) {
-            comp->m_task.setStarted(true);
-            comp->preamble();
-        }
-        //Bare components cannot block, so return to the scheduler
-        if (comp->m_queue.getNumMsgs() == 0) {
-            return;
-        }
-        ActiveComponentBase::MsgDispatchStatus loopStatus = comp->doDispatch();
-        switch (loopStatus) {
-            case ActiveComponentBase::MSG_DISPATCH_OK: // if normal message processing, continue
+    Os::Task::Status ActiveComponentBase::join(void** pointer) {
+       return this->m_task.join();
+    }
+
+    void ActiveComponentBase::s_taskStateMachine(void* component_pointer) {
+        FW_ASSERT(component_pointer != nullptr);
+        // cast void* back to active component
+        ActiveComponentBase* component = static_cast<ActiveComponentBase*>(component_pointer);
+
+        // Each invocation of this function runs a single stage of the thread lifecycle. This has moved the thread
+        // while loop to the top level such that it can be replaced by something else (e.g. cooperative thread
+        // dispatcher) and is not intrinsic to this code.
+        switch (component->m_stage) {
+            // The first stage the active component triggers the "preamble" call before moving into the dispatching
+            // stage of the component thread.
+            case Lifecycle::CREATED:
+                component->preamble();
+                component->m_stage = Lifecycle::DISPATCHING;
+                break;
+            // The second stage of the active component triggers the dispatching loop dispatching messages until an
+            // exit message is received.
+            case Lifecycle::DISPATCHING:
+                if (component->dispatch() == MsgDispatchStatus::MSG_DISPATCH_EXIT) {
+                    component->m_stage = Lifecycle::FINALIZING;
+                }
                 break;
-            case ActiveComponentBase::MSG_DISPATCH_EXIT:
-                comp->finalizer();
-                comp->m_task.setStarted(false);
+            // The second-to-last stage is where the finalizer is called. This will transition to the final stage
+            // automatically after the finalizer is called
+            case Lifecycle::FINALIZING:
+                component->finalizer();
+                component->m_stage = Lifecycle::DONE;
+                break;
+            // The last stage does nothing, cooperative tasks live here forever, threaded tasks exit on this condition
+            case Lifecycle::DONE:
                 break;
             default:
-                FW_ASSERT(0,static_cast<NATIVE_INT_TYPE>(loopStatus));
+                FW_ASSERT(0);
+                break;
         }
     }
-    void ActiveComponentBase::s_baseTask(void* ptr) {
-        // cast void* back to active component
-        ActiveComponentBase* comp = static_cast<ActiveComponentBase*> (ptr);
-        // indicated that task is started
-        comp->m_task.setStarted(true);
-        // print out message when task is started
-        // printf("Active Component %s task started.\n",comp->getObjName());
-        // call preamble
-        comp->preamble();
-        // call main task loop until exit or error
-        comp->loop();
-        // if main loop exits, call finalizer
-        comp->finalizer();
-    }
 
-    void ActiveComponentBase::loop() {
-
-        bool quitLoop = false;
-        while (!quitLoop) {
-            MsgDispatchStatus loopStatus = this->doDispatch();
-            switch (loopStatus) {
-                case MSG_DISPATCH_OK: // if normal message processing, continue
-                    break;
-                case MSG_DISPATCH_EXIT:
-                    quitLoop = true;
-                    break;
-                default:
-                    FW_ASSERT(0,static_cast<NATIVE_INT_TYPE>(loopStatus));
-            }
+    void ActiveComponentBase::s_taskLoop(void* component_pointer) {
+        FW_ASSERT(component_pointer != nullptr);
+        ActiveComponentBase* component = static_cast<ActiveComponentBase*>(component_pointer);
+        // A non-cooperative task switching implementation is just a while-loop around the active component
+        // state-machine. Here the while loop is at top-level.
+        while (component->m_stage != ActiveComponentBase::Lifecycle::DONE) {
+            ActiveComponentBase::s_taskStateMachine(component);
         }
+    }
 
+    ActiveComponentBase::MsgDispatchStatus ActiveComponentBase::dispatch() {
+        // Cooperative tasks should return rather than block when no messages are available
+        if (this->m_task.isCooperative() and m_queue.getNumMsgs() == 0) {
+            return MsgDispatchStatus::MSG_DISPATCH_EMPTY;
+        }
+       return this->doDispatch();
     }
 
     void ActiveComponentBase::preamble() {
diff --git a/Fw/Comp/ActiveComponentBase.hpp b/Fw/Comp/ActiveComponentBase.hpp
index 1b19635547..d9ee8d3ccf 100644
--- a/Fw/Comp/ActiveComponentBase.hpp
+++ b/Fw/Comp/ActiveComponentBase.hpp
@@ -23,28 +23,39 @@ class ActiveComponentBase : public QueuedComponentBase {
                Os::Task::ParamType stackSize = Os::Task::TASK_DEFAULT,
                Os::Task::ParamType cpuAffinity = Os::Task::TASK_DEFAULT,
                Os::Task::ParamType identifier =
-                   Os::Task::TASK_DEFAULT);       //!< called by instantiator when task is to be started
+               Os::Task::TASK_DEFAULT);           //!< called by instantiator when task is to be started
     void exit();                                  //!< exit task in active component
-    Os::Task::TaskStatus join(void** value_ptr);  //!< provide return value of thread if value_ptr is not NULL
+    Os::Task::Status join();  //!< Join the thread
+    DEPRECATED(Os::Task::Status join(void** value_ptr), "Switch to .join()");  //!< Join to thread with discarded value_ptr
 
     enum {
         ACTIVE_COMPONENT_EXIT  //!< message to exit active component task
     };
 
   PROTECTED:
+    //! Tracks the lifecycle of the component
+    enum Lifecycle {
+        CREATED,     //!< Initial stage, call preamble
+        DISPATCHING, //!< Component is dispatching messages
+        FINALIZING, //!< Penultimate stage, call finalizer
+        DONE,       //!< Done, doing nothing
+    };
+
     explicit ActiveComponentBase(const char* name);  //!< Constructor
     virtual ~ActiveComponentBase();                  //!< Destructor
     void init(NATIVE_INT_TYPE instance);             //!< initialization code
     virtual void preamble();   //!< A function that will be called before the event loop is entered
-    virtual void loop();       //!< The function that will loop dispatching messages
+    MsgDispatchStatus dispatch(); //!< The function that will dispatching messages
     virtual void finalizer();  //!< A function that will be called after exiting the loop
     Os::Task m_task;           //!< task object for active component
+
 #if FW_OBJECT_TO_STRING == 1
     virtual void toString(char* str, NATIVE_INT_TYPE size);  //!< create string description of component
 #endif
   PRIVATE:
-    static void s_baseTask(void*);      //!< function provided to task class for new thread.
-    static void s_baseBareTask(void*);  //!< function provided to task class for new thread.
+    Lifecycle m_stage;         //!< Lifecycle stage of the component
+    static void s_taskStateMachine(void*); //!< Task lifecycle state machine
+    static void s_taskLoop(void*);  //!< Standard multi-threading task loop
 };
 
 }  // namespace Fw
diff --git a/Os/Baremetal/File.cpp b/Os/Baremetal/File.cpp
deleted file mode 100644
index fed6dee7b3..0000000000
--- a/Os/Baremetal/File.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#include <FpConfig.hpp>
-#include <Os/File.hpp>
-#include <Fw/Types/Assert.hpp>
-
-
-namespace Os {
-
-    File::File() :m_fd(0),m_mode(OPEN_NO_MODE),m_lastError(0) {}
-
-    File::~File() {}
-
-    File::Status File::open(const char* fileName, File::Mode mode) {
-        return NOT_OPENED;
-    }
-
-    File::Status File::open(const char* fileName, File::Mode mode, bool include_excl) {
-        return NOT_OPENED;
-    }
-
-    bool File::isOpen() {
-      return false;
-    }
-
-    File::Status File::seek(NATIVE_INT_TYPE offset, bool absolute) {
-        return NOT_OPENED;
-    }
-
-    File::Status File::read(void * buffer, NATIVE_INT_TYPE &size, bool waitForFull) {
-        return NOT_OPENED;
-    }
-
-    File::Status File::write(const void * buffer, NATIVE_INT_TYPE &size, bool waitForDone) {
-        return NOT_OPENED;
-    }
-
-    void File::close() {}
-
-    NATIVE_INT_TYPE File::getLastError() {
-        return 0;
-    }
-
-    const char* File::getLastErrorString() {
-        return "FileBad";
-    }
-
-}
diff --git a/Os/Baremetal/Task.cpp b/Os/Baremetal/Task.cpp
deleted file mode 100644
index a9e19dfa06..0000000000
--- a/Os/Baremetal/Task.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#include <Fw/Comp/ActiveComponentBase.hpp>
-#include <Os/Task.hpp>
-#include <Os/Baremetal/TaskRunner/BareTaskHandle.hpp>
-#include <Fw/Types/Assert.hpp>
-#include <cstdio>
-#include <new>
-
-namespace Os {
-
-Task::Task() :
-    m_handle(0),
-    m_identifier(0),
-    m_affinity(-1),
-    m_started(false),
-    m_suspendedOnPurpose(false)
-{}
-
-Task::TaskStatus Task::start(const Fw::StringBase &name, taskRoutine routine, void* arg, ParamType priority, ParamType stackSize, ParamType cpuAffinity, ParamType identifier) {
-    //Get a task handle, and set it up
-    BareTaskHandle* handle = new(std::nothrow) BareTaskHandle();
-    if (handle == nullptr) {
-       return Task::TASK_UNKNOWN_ERROR;
-    }
-    //Set handle member variables
-    handle->m_enabled = true;
-    handle->m_priority = priority;
-    handle->m_routine = routine;
-    handle->m_argument = arg;
-    //Register this task using our custom task handle
-    m_handle = reinterpret_cast<POINTER_CAST>(handle);
-    this->m_name = "BR_";
-    this->m_name += name;
-    this->m_identifier = identifier;
-    // If a registry has been registered, register task
-    if (Task::s_taskRegistry) {
-        Task::s_taskRegistry->addTask(this);
-    }
-    //Running the task the first time allows setup activities for the task
-    handle->m_routine(handle->m_argument);
-    return Task::TASK_OK;
-}
-
-Task::TaskStatus Task::delay(NATIVE_UINT_TYPE milliseconds)
-{
-    //Task delays are a bad idea in baremetal tasks
-    return Task::TASK_DELAY_ERROR;
-}
-
-Task::~Task() {
-    if (this->m_handle) {
-        delete reinterpret_cast<BareTaskHandle*>(this->m_handle);
-    }
-    // If a registry has been registered, remove task
-    if (Task::s_taskRegistry) {
-        Task::s_taskRegistry->removeTask(this);
-    }
-}
-
-void Task::suspend(bool onPurpose) {
-    FW_ASSERT(reinterpret_cast<BareTaskHandle*>(this->m_handle) != nullptr);
-    reinterpret_cast<BareTaskHandle*>(this->m_handle)->m_enabled = false;
-}
-
-void Task::resume() {
-    FW_ASSERT(reinterpret_cast<BareTaskHandle*>(this->m_handle) != nullptr);
-    reinterpret_cast<BareTaskHandle*>(this->m_handle)->m_enabled = true;
-}
-
-bool Task::isSuspended() {
-    FW_ASSERT(reinterpret_cast<BareTaskHandle*>(this->m_handle) != nullptr);
-    return !reinterpret_cast<BareTaskHandle*>(this->m_handle)->m_enabled;
-}
-
-Task::TaskStatus Task::join(void **value_ptr) {
-    return TASK_OK;
-}
-
-}
diff --git a/Os/CMakeLists.txt b/Os/CMakeLists.txt
index 529e0e149e..1411c8ecd2 100644
--- a/Os/CMakeLists.txt
+++ b/Os/CMakeLists.txt
@@ -26,7 +26,7 @@ set(MOD_DEPS
 # Basic source files used in every OSAL layer.  Contains common code and helpers.
 set(SOURCE_FILES
     "${CMAKE_CURRENT_LIST_DIR}/IntervalTimerCommon.cpp"
-    "${CMAKE_CURRENT_LIST_DIR}/TaskCommon.cpp"
+#    "${CMAKE_CURRENT_LIST_DIR}/TaskCommon.cpp"
     "${CMAKE_CURRENT_LIST_DIR}/QueueCommon.cpp"
     "${CMAKE_CURRENT_LIST_DIR}/IPCQueueCommon.cpp"
     "${CMAKE_CURRENT_LIST_DIR}/SimpleQueueRegistry.cpp"
@@ -36,6 +36,7 @@ set(SOURCE_FILES
 
     # Refactored common files
     "${CMAKE_CURRENT_LIST_DIR}/File.cpp"
+    "${CMAKE_CURRENT_LIST_DIR}/Task.cpp"
 )
 # Check for default logger
 if (NOT FPRIME_DISABLE_DEFAULT_LOGGER)
@@ -51,13 +52,12 @@ if (FPRIME_USE_POSIX)
         "${CMAKE_CURRENT_LIST_DIR}/Pthreads/BufferQueueCommon.cpp"
         "${CMAKE_CURRENT_LIST_DIR}/Pthreads/PriorityBufferQueue.cpp"
         "${CMAKE_CURRENT_LIST_DIR}/Pthreads/MaxHeap/MaxHeap.cpp"
-        "${CMAKE_CURRENT_LIST_DIR}/Posix/Task.cpp"
+#        "${CMAKE_CURRENT_LIST_DIR}/Posix/Task.cpp"
         "${CMAKE_CURRENT_LIST_DIR}/Linux/InterruptLock.cpp"
         "${CMAKE_CURRENT_LIST_DIR}/Linux/WatchdogTimer.cpp"
         "${CMAKE_CURRENT_LIST_DIR}/Posix/IntervalTimer.cpp"
         "${CMAKE_CURRENT_LIST_DIR}/Posix/Mutex.cpp"
         "${CMAKE_CURRENT_LIST_DIR}/Linux/FileSystem.cpp"
-        "${CMAKE_CURRENT_LIST_DIR}/Posix/TaskId.cpp"
         "${CMAKE_CURRENT_LIST_DIR}/Linux/Directory.cpp"
     )
 endif()
@@ -89,13 +89,11 @@ if (FPRIME_USE_BAREMETAL_SCHEDULER)
             list(REMOVE_ITEM SOURCE_FILES "${ITER_ITEM}")
         endif()
     endforeach()
-    list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Baremetal/File.cpp")
     list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Baremetal/FileSystem.cpp")
     list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Baremetal/IntervalTimer.cpp")
     list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Baremetal/Mutex.cpp")
     list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Baremetal/Queue.cpp")
     list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Baremetal/SystemResources.cpp")
-    list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Baremetal/Task.cpp")
 endif()
 register_fprime_module()
 require_fprime_implementation(Os/File)
@@ -107,7 +105,6 @@ set(UT_SOURCE_FILES
   "${CMAKE_CURRENT_LIST_DIR}/test/ut/IntervalTimerTest.cpp"
   "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsFileSystemTest.cpp"
   "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsValidateFileTest.cpp"
-  "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsTaskTest.cpp"
   "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsSystemResourcesTest.cpp"
   "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsMutexBasicLockableTest.cpp"
 )
diff --git a/Os/Delegate.hpp b/Os/Delegate.hpp
new file mode 100644
index 0000000000..0c977f6153
--- /dev/null
+++ b/Os/Delegate.hpp
@@ -0,0 +1,107 @@
+// ======================================================================
+// \title Os/Delegate.hpp
+// \brief helper functions to ease correct getDelegate implementations
+// ======================================================================
+#include <new>
+#include <type_traits>
+#include "Fw/Types/Assert.hpp"
+#include "Os/Os.hpp"
+#ifndef OS_DELEGATE_HPP_
+#define OS_DELEGATE_HPP_
+namespace Os {
+namespace Delegate {
+
+//! \brief Make a delegate of type Interface using Implementation without copy-constructor support (generic function)
+//!
+//! This function is a generic implementation of the `getDelegate` functions provided for each function within Os. This
+//! is templated over two types: Interface (e.g. TaskInterface) the interface the delegate supports, and Implementation
+//! the implementation of the interface. This function takes care of the critical requirements of the getDelegate
+//! function:
+//!   1. Ensure Implementation is derived from Interface
+//!   2. Ensure Implementation fits within FW_HANDLE_MAX_SIZE
+//!   3. Ensure Implementation alignment fits within FW_HANDLE_ALIGNMENT
+//!   4. Performs the correct placement new for normal constructors
+//!   5. Ensure the returned pointer is not nullptr
+//!
+//! Implementors of a `getDelegate` function may use this function by calling it and returning the result.
+//!
+//! > Neither Interface nor Implementation is allowed to support copy-constructors
+//!
+//! Example: TaskInterface getDelegate (Without Copy-Constructor)
+//!
+//! ```c++
+//! #include "Os/Delegate.hpp"
+//!
+//! namespace Os {
+//! TaskInterface* TaskInterface::getDelegate(HandleStorage& aligned_new_memory) {
+//!   return Os::Delegate::makeDelegate<TaskInterface, Os::Posix::Task::PosixTask>(aligned_new_memory);
+//! }
+//! }
+//! ```
+//! \tparam Interface: interface the delegate supports (e.g. TaskInterface)
+//! \tparam Implementation: implementation class of the delegate (e.g. PosixTask)
+//! \param aligned_new_memory: memory to be filled via placement new call
+//! \return pointer to implementation result of placement new
+template <class Interface, class Implementation>
+inline Interface* makeDelegate(HandleStorage& aligned_new_memory) {
+    // Ensure prerequisites before performing placement new
+    static_assert(std::is_base_of<Interface, Implementation>::value, "Implementation must derive from Interface");
+    static_assert(sizeof(Implementation) <= FW_HANDLE_MAX_SIZE, "Handle size not large enough");
+    static_assert((FW_HANDLE_ALIGNMENT % alignof(Implementation)) == 0, "Handle alignment invalid");
+    // Placement new the object and ensure non-null result
+    Implementation* interface = new (aligned_new_memory) Implementation;
+    FW_ASSERT(interface != nullptr);
+    return interface;
+}
+
+//! \brief Make a delegate of type Interface using Implementation with copy-constructor support (generic function)
+//!
+//! This function is a generic implementation of the `getDelegate` functions provided for each function within Os. This
+//! is templated over two types: Interface (e.g. TaskInterface) the interface the delegate supports, and Implementation
+//! the implementation of the interface. This function takes care of the critical requirements of the getDelegate
+//! function:
+//!   1. Ensure Implementation is derived from Interface
+//!   2. Ensure Implementation fits within FW_HANDLE_MAX_SIZE
+//!   3. Ensure Implementation alignment fits within FW_HANDLE_ALIGNMENT
+//!   4. Performs the correct placement new for normal and copy constructors
+//!   5. Ensure the returned pointer is not nullptr
+//!
+//! Implementors of a `getDelegate` function may use this function by calling it and returning the result.
+//!
+//! Example: FileInterface getDelegate Supporting Copy-Constructor
+//!
+//! ```c++
+//! #include "Os/Delegate.hpp"
+//!
+//! namespace Os {
+//! FileInterface* FileInterface::getDelegate(HandleStorage& aligned_new_memory, const FileInterface* to_copy) {
+//!   return Os::Delegate::makeDelegate<FileInterface, Os::Posix::File::PosixFile>(aligned_new_memory, to_copy);
+//! }
+//! }
+//! ```
+//! \tparam Interface: interface the delegate supports (e.g. FileInterface)
+//! \tparam Implementation: implementation class of the delegate (e.g. PosixFile)
+//! \param aligned_new_memory: memory to be filled via placement new call
+//! \return pointer to implementation result of placement new
+//! \param to_copy: pointer to Interface to be copied by copy constructor
+//! \return pointer to implementation result of placement new
+template <class Interface, class Implementation>
+inline Interface* makeDelegate(HandleStorage& aligned_new_memory, const Interface* to_copy) {
+    const Implementation* copy_me = reinterpret_cast<const Implementation*>(to_copy);
+    // Ensure prerequisites before performing placement new
+    static_assert(std::is_base_of<Interface, Implementation>::value, "Implementation must derive from Interface");
+    static_assert(sizeof(Implementation) <= sizeof(aligned_new_memory), "Handle size not large enough");
+    static_assert((FW_HANDLE_ALIGNMENT % alignof(Implementation)) == 0, "Handle alignment invalid");
+    // Placement new the object and ensure non-null result
+    Implementation* interface = nullptr;
+    if (to_copy == nullptr) {
+        interface = new (aligned_new_memory) Implementation;
+    } else {
+        interface = new (aligned_new_memory) Implementation(*copy_me);
+    }
+    FW_ASSERT(interface != nullptr);
+    return interface;
+}
+}  // namespace Delegate
+}  // namespace Os
+#endif
diff --git a/Os/Event.hpp b/Os/Event.hpp
deleted file mode 100644
index e8e41b8a46..0000000000
--- a/Os/Event.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-// File: Event.hpp
-// Author: Nathan Serafin (nathan.serafin@jpl.nasa.gov)
-// Date: 27 July, 2018
-//
-// OS-independent wrapper for events.
-
-#ifndef EVENT_HPP
-#define EVENT_HPP
-
-#include <FpConfig.hpp>
-#include <Os/TaskId.hpp>
-
-namespace Os {
-    class Event {
-        public:
-            enum Timeout {
-                EV_NO_WAIT      = 0,
-                EV_WAIT_FOREVER = -1,
-            };
-
-            enum Options {
-                EV_EVENTS_WAIT_ALL      = 0x0U,
-                EV_EVENTS_WAIT_ANY      = 0x1U,
-                EV_EVENTS_RETURN_ALL    = 0x2U,
-                EV_EVENTS_KEEP_UNWANTED = 0x4U,
-                EV_EVENTS_FETCH         = 0x80U,
-            };
-
-            static I32 send(const TaskId& tid, const U32 events);
-            static I32 receive(const U32 events, const U8 options,
-                               const I32 timeout, U32* eventsReceived);
-            static I32 clear();
-    };
-}
-
-#endif
diff --git a/Os/File.cpp b/Os/File.cpp
index 40ebea9ede..4d8c1aab8a 100644
--- a/Os/File.cpp
+++ b/Os/File.cpp
@@ -10,7 +10,7 @@ extern "C" {
 }
 namespace Os {
 
-File::File() : m_crc_buffer(), m_handle_storage(), m_delegate(*FileInterface::getDelegate(&m_handle_storage[0])) {
+File::File() : m_crc_buffer(), m_handle_storage(), m_delegate(*FileInterface::getDelegate(m_handle_storage)) {
     FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
 }
 
@@ -28,7 +28,7 @@ File::File(const File& other) :
     m_crc(other.m_crc),
     m_crc_buffer(),
     m_handle_storage(),
-    m_delegate(*FileInterface::getDelegate(&m_handle_storage[0], &other.m_delegate)) {
+    m_delegate(*FileInterface::getDelegate(m_handle_storage, &other.m_delegate)) {
     FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
 }
 
@@ -37,7 +37,7 @@ File& File::operator=(const File& other) {
         this->m_mode = other.m_mode;
         this->m_path = other.m_path;
         this->m_crc = other.m_crc;
-        this->m_delegate = *FileInterface::getDelegate(&m_handle_storage[0], &other.m_delegate);
+        this->m_delegate = *FileInterface::getDelegate(m_handle_storage, &other.m_delegate);
     }
     return *this;
 }
diff --git a/Os/File.hpp b/Os/File.hpp
index 84695cf3a9..0e7c926851 100644
--- a/Os/File.hpp
+++ b/Os/File.hpp
@@ -6,6 +6,7 @@
 #define Os_File_hpp_
 
 #include <FpConfig.hpp>
+#include <Os/Os.hpp>
 
 namespace Os {
     //! \brief base implementation of FileHandle
@@ -206,14 +207,12 @@ namespace Os {
             //!
             //! \return result of placement new, must be equivalent to `aligned_placement_new_memory`
             //!
-            static FileInterface* getDelegate(U8* aligned_placement_new_memory, const FileInterface* to_copy=nullptr);
+            static FileInterface* getDelegate(HandleStorage& aligned_placement_new_memory, const FileInterface* to_copy=nullptr);
     };
 
 
     class File final : public FileInterface {
       public:
-        // Required for access to m_handle_storage for static assertions against actual storage
-        friend FileInterface* FileInterface::getDelegate(U8*, const FileInterface*);
         //! \brief constructor
         //!
         File();
@@ -485,7 +484,7 @@ namespace Os {
         // opaque and thus normal allocation cannot be done. Instead, we allow the implementor to store then handle in
         // the byte-array here and set `handle` to that address for storage.
         //
-        alignas(FW_HANDLE_ALIGNMENT) U8 m_handle_storage[FW_HANDLE_MAX_SIZE]; //!< Storage for aligned FileHandle data
+        alignas(FW_HANDLE_ALIGNMENT) HandleStorage m_handle_storage; //!< Storage for aligned FileHandle data
         FileInterface& m_delegate; //!< Delegate for the real implementation
     };
 }
diff --git a/Os/Models/CMakeLists.txt b/Os/Models/CMakeLists.txt
index 428c495955..d97d72449d 100644
--- a/Os/Models/CMakeLists.txt
+++ b/Os/Models/CMakeLists.txt
@@ -8,5 +8,6 @@
 ####
 set(SOURCE_FILES
     "${CMAKE_CURRENT_LIST_DIR}/File.fpp"
+    "${CMAKE_CURRENT_LIST_DIR}/Task.fpp"
 )
 register_fprime_module()
diff --git a/Os/Models/Models.hpp b/Os/Models/Models.hpp
index 2cc9457446..bdfe18c7b8 100644
--- a/Os/Models/Models.hpp
+++ b/Os/Models/Models.hpp
@@ -5,6 +5,9 @@
 
 #include "Os/Models/FileStatusEnumAc.hpp"
 #include "Os/Models/FileModeEnumAc.hpp"
+#include "Os/Models/TaskStatusEnumAc.hpp"
+#include "Os/File.hpp"
+#include "Os/Task.hpp"
 
 #ifndef OS_MODELS_MODELS_HPP
 #define OS_MODELS_MODELS_HPP
@@ -53,4 +56,27 @@ static_assert(static_cast<Os::FileMode::T>(Os::File::Mode::OPEN_SYNC_WRITE) == O
 static_assert(static_cast<Os::FileMode::T>(Os::File::Mode::OPEN_APPEND) == Os::FileMode::T::OPEN_APPEND,
               "File mode and FPP shadow enum do not Mode");
 
+static_assert(static_cast<Os::TaskStatus::T>(Os::Task::Status::OP_OK) == Os::TaskStatus::T::OP_OK,
+              "Task status and FPP shadow enum do not match");
+static_assert(static_cast<Os::TaskStatus::T>(Os::Task::Status::INVALID_HANDLE) == Os::TaskStatus::T::INVALID_HANDLE,
+              "Task status and FPP shadow enum do not match");
+static_assert(static_cast<Os::TaskStatus::T>(Os::Task::Status::INVALID_PARAMS) == Os::TaskStatus::T::INVALID_PARAMS,
+              "Task status and FPP shadow enum do not match");
+static_assert(static_cast<Os::TaskStatus::T>(Os::Task::Status::INVALID_STACK) == Os::TaskStatus::T::INVALID_STACK,
+              "Task status and FPP shadow enum do not match");
+static_assert(static_cast<Os::TaskStatus::T>(Os::Task::Status::UNKNOWN_ERROR) == Os::TaskStatus::T::UNKNOWN_ERROR,
+              "Task status and FPP shadow enum do not match");
+static_assert(static_cast<Os::TaskStatus::T>(Os::Task::Status::INVALID_AFFINITY) == Os::TaskStatus::T::INVALID_AFFINITY,
+              "Task status and FPP shadow enum do not match");
+static_assert(static_cast<Os::TaskStatus::T>(Os::Task::Status::DELAY_ERROR) == Os::TaskStatus::T::DELAY_ERROR,
+              "Task status and FPP shadow enum do not match");
+static_assert(static_cast<Os::TaskStatus::T>(Os::Task::Status::JOIN_ERROR) == Os::TaskStatus::T::JOIN_ERROR,
+              "Task status and FPP shadow enum do not match");
+static_assert(static_cast<Os::TaskStatus::T>(Os::Task::Status::ERROR_RESOURCES) == Os::TaskStatus::T::ERROR_RESOURCES,
+              "Task status and FPP shadow enum do not match");
+static_assert(static_cast<Os::TaskStatus::T>(Os::Task::Status::ERROR_PERMISSION) == Os::TaskStatus::T::ERROR_PERMISSION,
+              "Task status and FPP shadow enum do not match");
+static_assert(static_cast<Os::TaskStatus::T>(Os::Task::Status::INVALID_STATE) == Os::TaskStatus::T::INVALID_STATE,
+              "Task status and FPP shadow enum do not match");
+
 #endif // OS_MODELS_MODELS_HPP
diff --git a/Os/Models/Task.fpp b/Os/Models/Task.fpp
new file mode 100644
index 0000000000..a91d252cd8
--- /dev/null
+++ b/Os/Models/Task.fpp
@@ -0,0 +1,22 @@
+# ======================================================================
+# \title Os/Models/task.fpp
+# \brief FPP type definitions for Os/Task.hpp concepts
+# ======================================================================
+
+module Os {
+@ FPP shadow-enum representing Os::Task::Status
+enum TaskStatus {
+    OP_OK, @< message sent/received okay
+    INVALID_HANDLE, @< Task handle invalid
+    INVALID_PARAMS, @< started task with invalid parameters
+    INVALID_STACK, @< started with invalid stack size
+    UNKNOWN_ERROR, @< unexpected error return value
+    INVALID_AFFINITY, @< unable to set the task affinity
+    DELAY_ERROR, @< error trying to delay the task
+    JOIN_ERROR, @< error trying to join the task
+    ERROR_RESOURCES, @< unable to allocate more tasks
+    ERROR_PERMISSION, @< permissions error setting-up tasks
+    INVALID_STATE, @< Task is in an invalid state for the operation
+}
+}
+
diff --git a/Os/Os.hpp b/Os/Os.hpp
new file mode 100644
index 0000000000..af8adce9fb
--- /dev/null
+++ b/Os/Os.hpp
@@ -0,0 +1,11 @@
+// ======================================================================
+// \title Os/Os.hpp
+// \brief common definitions for the OSAL layer
+// ======================================================================
+#ifndef OS_OS_HPP_
+#define OS_OS_HPP_
+#include "FpConfig.h"
+
+//! Storage type for OSAL handles
+typedef U8 HandleStorage[FW_HANDLE_MAX_SIZE];
+#endif
diff --git a/Os/Posix/CMakeLists.txt b/Os/Posix/CMakeLists.txt
index 8babf771b9..85cf7116ec 100644
--- a/Os/Posix/CMakeLists.txt
+++ b/Os/Posix/CMakeLists.txt
@@ -7,26 +7,62 @@
 ####
 restrict_platforms(Posix)
 add_custom_target("${FPRIME_CURRENT_MODULE}")
+
+#### Shared Section ####
+set(SOURCE_FILES
+    "${CMAKE_CURRENT_LIST_DIR}/error.cpp"
+)
+set(HEADER_FILES
+    "${CMAKE_CURRENT_LIST_DIR}/error.hpp"
+)
+register_fprime_module(Os_Posix_Shared)
+
+#### Os/File/Posix Section ####
 set(SOURCE_FILES
     "${CMAKE_CURRENT_LIST_DIR}/File.cpp"
-    "${CMAKE_CURRENT_LIST_DIR}/errno.cpp"
 )
 set(HEADER_FILES
-    "${CMAKE_CURRENT_LIST_DIR}/errno.hpp"
     "${CMAKE_CURRENT_LIST_DIR}/File.hpp"
 )
+set(MOD_DEPS Os_Posix_Shared)
 register_fprime_module(Os_File_Posix)
 register_fprime_implementation(Os/File Os_File_Posix "${CMAKE_CURRENT_LIST_DIR}/DefaultFile.cpp")
 
+#### Os/File/Posix Unit Tests ####
 set(UT_SOURCE_FILES
-    "${CMAKE_CURRENT_LIST_DIR}/../test/ut/file/CommonTests.cpp"
-    "${CMAKE_CURRENT_LIST_DIR}/../test/ut/file/FileRules.cpp"
-    "${CMAKE_CURRENT_LIST_DIR}/test/ut/PosixFileTests.cpp"
+        "${CMAKE_CURRENT_LIST_DIR}/../test/ut/file/CommonTests.cpp"
+        "${CMAKE_CURRENT_LIST_DIR}/../test/ut/file/FileRules.cpp"
+        "${CMAKE_CURRENT_LIST_DIR}/test/ut/PosixFileTests.cpp"
 )
 set(UT_MOD_DEPS
-    Os
-    STest
-    Os_Test_File_SyntheticFileSystem
+        Os
+        STest
+        Os_Test_File_SyntheticFileSystem
 )
 choose_fprime_implementation(Os/File Os_File_Posix)
 register_fprime_ut(PosixFileTest)
+
+#### Os/Task/Posix Section ####
+set(SOURCE_FILES
+    "${CMAKE_CURRENT_LIST_DIR}/Task.cpp"
+)
+set(HEADER_FILES
+    "${CMAKE_CURRENT_LIST_DIR}/Task.hpp"
+)
+
+set(MOD_DEPS Os_Posix_Shared Fw/Time Os)
+register_fprime_module(Os_Task_Posix)
+register_fprime_implementation(Os/Task Os_Task_Posix "${CMAKE_CURRENT_LIST_DIR}/DefaultTask.cpp")
+
+#### Os/Task/Posix Unit Tests ####
+set(UT_SOURCE_FILES
+        "${CMAKE_CURRENT_LIST_DIR}/../test/ut/task/CommonTests.cpp"
+        "${CMAKE_CURRENT_LIST_DIR}/../test/ut/task/TaskRules.cpp"
+        "${CMAKE_CURRENT_LIST_DIR}/test/ut/PosixTaskTests.cpp"
+)
+set(UT_MOD_DEPS
+        Os
+        STest
+)
+choose_fprime_implementation(Os/Task Os_Task_Posix)
+register_fprime_ut(PosixTaskTest)
\ No newline at end of file
diff --git a/Os/Posix/DefaultFile.cpp b/Os/Posix/DefaultFile.cpp
index 4b7d486b27..da0d7278bc 100644
--- a/Os/Posix/DefaultFile.cpp
+++ b/Os/Posix/DefaultFile.cpp
@@ -4,31 +4,10 @@
 // ======================================================================
 #include "Os/File.hpp"
 #include "Os/Posix/File.hpp"
-#include "Fw/Types/Assert.hpp"
-#include <new>
+#include "Os/Delegate.hpp"
 
 namespace Os {
-//! \brief get implementation file interface for use as delegate
-//!
-//! Added via linker to ensure that the posix implementation of Os::File is the default.
-//!
-//! \param aligned_placement_new_memory: memory to placement-new into
-//! \return FileInterface pointer result of placement new
-//!
-FileInterface* FileInterface::getDelegate(U8* aligned_placement_new_memory, const FileInterface* to_copy) {
-    FW_ASSERT(aligned_placement_new_memory != nullptr);
-    const Os::Posix::File::PosixFile* copy_me = reinterpret_cast<const Os::Posix::File::PosixFile*>(to_copy);
-    // Placement-new the file handle into the opaque file-handle storage
-    static_assert(sizeof(Os::Posix::File::PosixFile) <= sizeof Os::File::m_handle_storage,
-            "Handle size not large enough");
-    static_assert((FW_HANDLE_ALIGNMENT % alignof(Os::Posix::File::PosixFile)) == 0, "Handle alignment invalid");
-    Os::Posix::File::PosixFile *interface = nullptr;
-    if (to_copy == nullptr) {
-        interface = new(aligned_placement_new_memory) Os::Posix::File::PosixFile;
-    } else {
-        interface = new(aligned_placement_new_memory) Os::Posix::File::PosixFile(*copy_me);
-    }
-    FW_ASSERT(interface != nullptr);
-    return interface;
+FileInterface* FileInterface::getDelegate(HandleStorage& aligned_new_memory, const FileInterface* to_copy) {
+    return Os::Delegate::makeDelegate<FileInterface, Os::Posix::File::PosixFile>(aligned_new_memory, to_copy);
 }
 }
diff --git a/Os/Posix/DefaultTask.cpp b/Os/Posix/DefaultTask.cpp
new file mode 100644
index 0000000000..7d5d163222
--- /dev/null
+++ b/Os/Posix/DefaultTask.cpp
@@ -0,0 +1,45 @@
+// ======================================================================
+// \title Os/Posix/DefaultTask.cpp
+// \brief sets default Os::Task to posix implementation via linker
+// ======================================================================
+#include <cerrno>
+#include "Os/Task.hpp"
+#include "Os/Posix/Task.hpp"
+#include "Os/Delegate.hpp"
+
+namespace Os {
+    Os::Task::Status TaskInterface::delay(Fw::Time interval) {
+        Os::Task::Status task_status = Os::Task::OP_OK;
+        timespec sleep_interval;
+        sleep_interval.tv_sec = interval.getSeconds();
+        sleep_interval.tv_nsec = interval.getUSeconds() * 1000;
+
+        timespec remaining_interval;
+        remaining_interval.tv_sec = 0;
+        remaining_interval.tv_nsec = 0;
+
+        while (true) {
+            PlatformIntType status = nanosleep(&sleep_interval, &remaining_interval);
+            // Success, return ok
+            if (0 == status) {
+                break;
+            }
+            // Interrupted, reset sleep and iterate
+            else if (EINTR == errno) {
+                sleep_interval = remaining_interval;
+                continue;
+            }
+            // Anything else is an error
+            else {
+                task_status = Os::Task::Status::DELAY_ERROR;
+                break;
+            }
+        }
+        return task_status;
+    }
+
+    TaskInterface* TaskInterface::getDelegate(HandleStorage& aligned_new_memory) {
+        return Os::Delegate::makeDelegate<TaskInterface, Os::Posix::Task::PosixTask>(aligned_new_memory);
+    }
+
+}
diff --git a/Os/Posix/File.cpp b/Os/Posix/File.cpp
index 7b92691687..e9ee211fe2 100644
--- a/Os/Posix/File.cpp
+++ b/Os/Posix/File.cpp
@@ -10,7 +10,7 @@
 #include <Os/File.hpp>
 #include <Os/Posix/File.hpp>
 #include <Fw/Types/Assert.hpp>
-#include <Os/Posix/errno.hpp>
+#include <Os/Posix/error.hpp>
 
 namespace Os {
 namespace Posix {
diff --git a/Os/Posix/Task.cpp b/Os/Posix/Task.cpp
index 816373cde5..2e29f7b29b 100644
--- a/Os/Posix/Task.cpp
+++ b/Os/Posix/Task.cpp
@@ -1,313 +1,214 @@
-#include <Os/Task.hpp>
-#include <Fw/Types/Assert.hpp>
-
-#include <pthread.h>
-#include <cerrno>
+// ======================================================================
+// \title Os/Posix/Task.cpp
+// \brief implementation of Posix implementation of Os::Task
+// ======================================================================
 #include <cstring>
-#include <ctime>
-#include <cstdio>
-#include <new>
-#include <sched.h>
+#include <unistd.h>
 #include <climits>
-#include <Fw/Logger/Logger.hpp>
-
-#ifdef TGT_OS_TYPE_LINUX
-#include <features.h>
-#endif
-
-static const NATIVE_INT_TYPE SCHED_POLICY = SCHED_RR;
-
-typedef void* (*pthread_func_ptr)(void*);
+#include <cerrno>
+#include <pthread.h>
 
-void* pthread_entry_wrapper(void* arg) {
-    FW_ASSERT(arg);
-    Os::Task::TaskRoutineWrapper *task = reinterpret_cast<Os::Task::TaskRoutineWrapper*>(arg);
-    FW_ASSERT(task->routine);
-    task->routine(task->arg);
-    return nullptr;
-}
+#include "Fw/Logger/Logger.hpp"
+#include "Fw/Types/Assert.hpp"
+#include "Os/Task.hpp"
+#include "Os/Posix/Task.hpp"
+#include "Os/Posix/error.hpp"
 
 namespace Os {
-
-    void validate_arguments(NATIVE_UINT_TYPE& priority, NATIVE_UINT_TYPE& stack, NATIVE_UINT_TYPE& affinity, bool expect_perm) {
-        const NATIVE_INT_TYPE min_priority = sched_get_priority_min(SCHED_POLICY);
-        const NATIVE_INT_TYPE max_priority = sched_get_priority_max(SCHED_POLICY);
-        // Check to ensure that these calls worked.  -1 is an error
-        if (min_priority < 0 or max_priority < 0) {
-            Fw::Logger::logMsg("[WARNING] Unable to determine min/max priority with error %s. Discarding priority.\n", reinterpret_cast<POINTER_CAST>(strerror(errno)));
-            priority = Os::Task::TASK_DEFAULT;
-        }
-        // Check priority attributes
-        if (!expect_perm and priority != Task::TASK_DEFAULT) {
-            Fw::Logger::logMsg("[WARNING] Task priority set and permissions unavailable. Discarding priority.\n");
-            priority = Task::TASK_DEFAULT; //Action: use constant
-        }
-        if (priority != Task::TASK_DEFAULT and priority < static_cast<Task::ParamType>(min_priority)) {
-            Fw::Logger::logMsg("[WARNING] Low task priority of %d being clamped to %d\n", priority, static_cast<POINTER_CAST>(min_priority));
-            priority = static_cast<NATIVE_UINT_TYPE>(min_priority);
-        }
-        if (priority != Task::TASK_DEFAULT and priority > static_cast<Task::ParamType>(max_priority)) {
-            Fw::Logger::logMsg("[WARNING] High task priority of %d being clamped to %d\n", priority, static_cast<NATIVE_UINT_TYPE>(max_priority));
-            priority = static_cast<NATIVE_UINT_TYPE>(max_priority);
-        }
-        // Check the stack
-        if (stack != Task::TASK_DEFAULT and stack < PTHREAD_STACK_MIN) {
-            Fw::Logger::logMsg("[WARNING] Stack size %d too small, setting to minimum of %d\n", stack, static_cast<NATIVE_UINT_TYPE>(PTHREAD_STACK_MIN));
-            stack = static_cast<NATIVE_UINT_TYPE>(PTHREAD_STACK_MIN);
-        }
-        // Check CPU affinity
-        if (!expect_perm and affinity != Task::TASK_DEFAULT) {
-            Fw::Logger::logMsg("[WARNING] Cpu affinity set and permissions unavailable. Discarding affinity.\n");
-            affinity = Task::TASK_DEFAULT;
-        }
+namespace Posix {
+namespace Task {
+    std::atomic<bool> PosixTask::s_permissions_reported(false);
+    static const PlatformIntType SCHED_POLICY = SCHED_RR;
+
+    typedef void* (*pthread_func_ptr)(void*);
+
+    void* pthread_entry_wrapper(void* wrapper_pointer) {
+        FW_ASSERT(wrapper_pointer != nullptr);
+        Os::Task::TaskRoutineWrapper& wrapper = *reinterpret_cast<Os::Task::TaskRoutineWrapper*>(wrapper_pointer);
+        wrapper.run(&wrapper);
+        return nullptr;
     }
 
-    Task::TaskStatus set_stack_size(pthread_attr_t& att, NATIVE_UINT_TYPE stack) {
-        // Set the stack size, if it has been supplied
-        if (stack != Task::TASK_DEFAULT) {
-            I32 stat = pthread_attr_setstacksize(&att, stack);
-            if (stat != 0) {
-                Fw::Logger::logMsg("pthread_attr_setstacksize: %s\n", reinterpret_cast<POINTER_CAST>(strerror(stat)));
-                return Task::TASK_INVALID_STACK;
-            }
-        }
-        return Task::TASK_OK;
+    PlatformIntType set_stack_size(pthread_attr_t& attributes, const Os::Task::Arguments& arguments) {
+        PlatformIntType status = PosixTaskHandle::SUCCESS;
+        FwSizeType stack = arguments.m_stackSize;
+        // Check for stack size multiple of page size
+        long page_size = sysconf(_SC_PAGESIZE);
+        if (page_size <= 0) {
+            Fw::Logger::logMsg(
+                    "[WARNING] %s could not determine page size %s. Skipping stack-size check.\n",
+                    reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())),
+                    reinterpret_cast<PlatformPointerCastType>(strerror(errno))
+            );
+        }
+        else if ((stack % static_cast<FwSizeType>(page_size)) != 0) {
+            // Round-down to nearest page size multiple
+            FwSizeType rounded = (stack / static_cast<FwSizeType>(page_size)) * static_cast<FwSizeType>(page_size);
+            Fw::Logger::logMsg(
+                    "[WARNING] %s stack size of %" PRI_FwSizeType " is not multiple of page size %ld, rounding to %" PRI_FwSizeType "\n",
+                    reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())),
+                    static_cast<PlatformPointerCastType>(stack),
+                    static_cast<PlatformPointerCastType>(page_size),
+                    static_cast<PlatformPointerCastType>(rounded)
+            );
+            stack = rounded;
+        }
+
+        // Clamp invalid stack sizes
+        if (stack <= static_cast<FwSizeType>(PTHREAD_STACK_MIN)) {
+            Fw::Logger::logMsg(
+                    "[WARNING] %s stack size of %" PRI_FwSizeType "  is too small, clamping to %" PRI_FwSizeType "\n",
+                    reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())),
+                    static_cast<PlatformPointerCastType>(stack),
+                    static_cast<PlatformPointerCastType>(static_cast<FwSizeType>(PTHREAD_STACK_MIN))
+            );
+            stack = static_cast<FwSizeType>(PTHREAD_STACK_MIN);
+        }
+        status = pthread_attr_setstacksize(&attributes, static_cast<size_t>(stack));
+        return status;
     }
 
-    Task::TaskStatus set_priority_params(pthread_attr_t& att, NATIVE_UINT_TYPE priority) {
-        if (priority != Task::TASK_DEFAULT) {
-            I32 stat = pthread_attr_setschedpolicy(&att, SCHED_POLICY);
-            if (stat != 0) {
-                Fw::Logger::logMsg("pthread_attr_setschedpolicy: %s\n", reinterpret_cast<POINTER_CAST>(strerror(stat)));
-                return Task::TASK_INVALID_PARAMS;
-            }
-
-            stat = pthread_attr_setinheritsched(&att, PTHREAD_EXPLICIT_SCHED);
-            if (stat != 0) {
-                Fw::Logger::logMsg("pthread_attr_setinheritsched: %s\n",
-                                   reinterpret_cast<POINTER_CAST>(strerror(stat)));
-                return Task::TASK_INVALID_PARAMS;
-            }
-
+    PlatformIntType set_priority_params(pthread_attr_t& attributes, const Os::Task::Arguments& arguments) {
+        const FwSizeType min_priority = static_cast<FwSizeType>(sched_get_priority_min(SCHED_POLICY));
+        const FwSizeType max_priority = static_cast<FwSizeType>(sched_get_priority_max(SCHED_POLICY));
+        PlatformIntType status = PosixTaskHandle::SUCCESS;
+        FwSizeType priority = arguments.m_priority;
+        // Clamp to minimum priority
+        if (priority < min_priority) {
+            Fw::Logger::logMsg("[WARNING] %s low task priority of %" PRI_FwSizeType " clamped to %" PRI_FwSizeType "\n",
+                               reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())),
+                               static_cast<PlatformPointerCastType>(priority),
+                               static_cast<PlatformPointerCastType>(min_priority));
+            priority = min_priority;
+        }
+        // Clamp to maximum priority
+        else if (priority > max_priority) {
+            Fw::Logger::logMsg("[WARNING] %s high task priority of %" PRI_FwSizeType " clamped to %" PRI_FwSizeType "\n",
+                               reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())),
+                               static_cast<PlatformPointerCastType>(priority),
+                               static_cast<PlatformPointerCastType>(max_priority));
+            priority = max_priority;
+        }
+
+        // Set attributes required for priority
+        status = pthread_attr_setschedpolicy(&attributes, SCHED_POLICY);
+        if (status == PosixTaskHandle::SUCCESS) {
+            status = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED);
+        }
+        if (status == PosixTaskHandle::SUCCESS) {
             sched_param schedParam;
             memset(&schedParam, 0, sizeof(sched_param));
-            schedParam.sched_priority =static_cast<int>(priority);
-            stat = pthread_attr_setschedparam(&att, &schedParam);
-            if (stat != 0) {
-                Fw::Logger::logMsg("pthread_attr_setschedparam: %s\n", reinterpret_cast<POINTER_CAST>(strerror(stat)));
-                return Task::TASK_INVALID_PARAMS;
-            }
+            schedParam.sched_priority = static_cast<PlatformIntType>(priority);
+            status = pthread_attr_setschedparam(&attributes, &schedParam);
         }
-        return Task::TASK_OK;
+        return status;
     }
 
-    Task::TaskStatus set_cpu_affinity(pthread_attr_t& att, NATIVE_UINT_TYPE cpuAffinity) {
-        if (cpuAffinity != Task::TASK_DEFAULT) {
-#if TGT_OS_TYPE_LINUX && __GLIBC__
-            cpu_set_t cpuset;
-            CPU_ZERO(&cpuset);
-            CPU_SET(cpuAffinity, &cpuset);
-
-            I32 stat = pthread_attr_setaffinity_np(&att, sizeof(cpu_set_t), &cpuset);
-            if (stat != 0) {
-                Fw::Logger::logMsg("pthread_setaffinity_np: %i %s\n", cpuAffinity,
-                                   reinterpret_cast<POINTER_CAST>(strerror(stat)));
-                return Task::TASK_INVALID_PARAMS;
-            }
-#elif TGT_OS_TYPE_LINUX
-            Fw::Logger::logMsg("[WARNING] Setting CPU affinity is only available on Linux with glibc\n");
+    PlatformIntType set_cpu_affinity(pthread_attr_t& attributes, const Os::Task::Arguments& arguments) {
+        PlatformIntType status = 0;
+// Feature set check for _GNU_SOURCE before using GNU only features
+#ifdef _GNU_SOURCE
+        const FwSizeType affinity = arguments.m_cpuAffinity;
+        cpu_set_t cpu_set;
+        CPU_ZERO(&cpu_set);
+        CPU_SET(static_cast<PlatformIntType>(affinity), &cpu_set);
+
+        // According to the man-page this function sets errno rather than returning an error status like other functions
+        status = pthread_attr_setaffinity_np(&attributes, sizeof(cpu_set_t), &cpu_set);
+        status = (status == PosixTaskHandle::SUCCESS) ? status : errno;
 #else
-            Fw::Logger::logMsg("[WARNING] Setting CPU affinity is only available on Linux\n");
+            Fw::Logger::logMsg("[WARNING] %s setting CPU affinity is only available with GNU pthreads\n",
+                               reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())));
 #endif
-        }
-        return Task::TASK_OK;
+        return status;
     }
 
-    Task::TaskStatus create_pthread(NATIVE_UINT_TYPE priority, NATIVE_UINT_TYPE stackSize, NATIVE_UINT_TYPE cpuAffinity, pthread_t*& tid, void* arg, bool expect_perm) {
-        Task::TaskStatus tStat = Task::TASK_OK;
-        validate_arguments(priority, stackSize, cpuAffinity, expect_perm);
-        pthread_attr_t att;
-        memset(&att,0, sizeof(att));
-
-
-        I32 stat = pthread_attr_init(&att);
-        if (stat != 0) {
-            Fw::Logger::logMsg("pthread_attr_init: (%d): %s\n", static_cast<POINTER_CAST>(stat), reinterpret_cast<POINTER_CAST>(strerror(stat)));
-            return Task::TASK_INVALID_PARAMS;
-        }
-
-        // Handle setting stack size
-        tStat = set_stack_size(att, stackSize);
-        if (tStat != Task::TASK_OK) {
-            return tStat;
+    Os::Task::Status PosixTask::create(const Os::Task::Arguments& arguments, const PosixTask::PermissionExpectation permissions) {
+        PlatformIntType pthread_status = PosixTaskHandle::SUCCESS;
+        PosixTaskHandle& handle = this->m_handle;
+        const bool expect_permission = (permissions == EXPECT_PERMISSION);
+        // Initialize and clear pthread attributes
+        pthread_attr_t attributes;
+        memset(&attributes, 0, sizeof(attributes));
+        pthread_status = pthread_attr_init(&attributes);
+        if ((arguments.m_stackSize != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
+            pthread_status = set_stack_size(attributes, arguments);
         }
-
-
-        // Handle non-zero priorities
-        tStat = set_priority_params(att, priority);
-        if (tStat != Task::TASK_OK) {
-            return tStat;
+        if ((arguments.m_priority != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
+            pthread_status = set_priority_params(attributes, arguments);
         }
-
-        // Set affinity before creating thread:
-        tStat = set_cpu_affinity(att, cpuAffinity);
-        if (tStat != Task::TASK_OK) {
-            return tStat;
+        if ((arguments.m_cpuAffinity != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
+            pthread_status = set_cpu_affinity(attributes, arguments);
         }
-
-        tid = new pthread_t;
-        const char* message = nullptr;
-
-        stat = pthread_create(tid, &att, pthread_entry_wrapper, arg);
-        switch (stat) {
-            // Success, do nothing
-            case 0:
-                break;
-            case EINVAL:
-                message = "Invalid thread attributes specified";
-                tStat = Task::TASK_INVALID_PARAMS;
-                break;
-            case EPERM:
-                message = "Insufficient permissions to create thread. May not set thread priority without permission";
-                tStat = Task::TASK_ERROR_PERMISSION;
-                break;
-            case EAGAIN:
-                message = "Unable to allocate thread. Increase thread ulimit.";
-                tStat = Task::TASK_ERROR_RESOURCES;
-                break;
-            default:
-                message = "Unknown error";
-                tStat = Task::TASK_UNKNOWN_ERROR;
-                break;
+        if (pthread_status == PosixTaskHandle::SUCCESS) {
+            pthread_status = pthread_create(&handle.m_task_descriptor, &attributes, pthread_entry_wrapper,
+                                            arguments.m_routine_argument);
         }
-        (void)pthread_attr_destroy(&att);
-        if (stat != 0) {
-            delete tid;
-            tid = nullptr;
-            Fw::Logger::logMsg("pthread_create: %s. %s\n", reinterpret_cast<POINTER_CAST>(message), reinterpret_cast<POINTER_CAST>(strerror(stat)));
-            return tStat;
+        // Successful execution of all precious steps will result in a valid task handle
+        if (pthread_status == PosixTaskHandle::SUCCESS) {
+            handle.m_is_valid = true;
         }
-        return Task::TASK_OK;
-    }
 
-    Task::Task() : m_handle(reinterpret_cast<POINTER_CAST>(nullptr)), m_identifier(0), m_affinity(-1), m_started(false), m_suspendedOnPurpose(false), m_routineWrapper() {
+        (void) pthread_attr_destroy(&attributes);
+        return Posix::posix_status_to_task_status(pthread_status);
     }
 
-    Task::TaskStatus Task::start(const Fw::StringBase &name, taskRoutine routine, void* arg, ParamType priority, ParamType stackSize,  ParamType cpuAffinity, ParamType identifier) {
-        FW_ASSERT(routine);
+    void PosixTask::onStart() {}
 
-        this->m_name = "TP_";
-        this->m_name += name;
-        this->m_identifier = static_cast<NATIVE_INT_TYPE>(identifier);
-        // Setup functor wrapper parameters
-        this->m_routineWrapper.routine = routine;
-        this->m_routineWrapper.arg = arg;
-        pthread_t* tid;
+    Os::Task::Status PosixTask::start(const Arguments& arguments) {
+        FW_ASSERT(arguments.m_routine != nullptr);
 
         // Try to create thread with assuming permissions
-        TaskStatus status = create_pthread(priority, stackSize, cpuAffinity, tid, &this->m_routineWrapper, true);
+        Os::Task::Status status = this->create(arguments, PermissionExpectation::EXPECT_PERMISSION);
         // Failure due to permission automatically retried
-        if (status == TASK_ERROR_PERMISSION) {
-            Fw::Logger::logMsg("[WARNING] Insufficient Permissions:\n");
-            Fw::Logger::logMsg("[WARNING] Insufficient permissions to set task priority or set task CPU affinity on task %s. Creating task without priority nor affinity.\n", reinterpret_cast<POINTER_CAST>(m_name.toChar()));
-            Fw::Logger::logMsg("[WARNING] Please use no-argument <component>.start() calls, set priority/affinity to TASK_DEFAULT or ensure user has correct permissions for operating system.\n");
-            Fw::Logger::logMsg("[WARNING]      Note: future releases of fprime will fail when setting priority/affinity without sufficient permissions \n");
-            Fw::Logger::logMsg("\n");
-            status = create_pthread(priority, stackSize, cpuAffinity, tid, &this->m_routineWrapper, false); // Fallback with no permission
-        }
-        // Check for non-zero error code
-        if (status != TASK_OK) {
-            return status;
-        }
-        FW_ASSERT(tid != nullptr);
-
-        // Handle a successfully created task
-        this->m_handle = reinterpret_cast<POINTER_CAST>(tid);
-        Task::s_numTasks++;
-        // If a registry has been registered, register task
-        if (Task::s_taskRegistry) {
-            Task::s_taskRegistry->addTask(this);
+        if (status == Os::Task::Status::ERROR_PERMISSION) {
+            if (not PosixTask::s_permissions_reported) {
+                Fw::Logger::logMsg("\n");
+                Fw::Logger::logMsg("[NOTE] Task Permissions:\n");
+                Fw::Logger::logMsg("[NOTE]\n");
+                Fw::Logger::logMsg("[NOTE] You have insufficient permissions to create a task with priority and/or cpu affinity.\n");
+                Fw::Logger::logMsg("[NOTE] A task without priority and affinity will be created.\n");
+                Fw::Logger::logMsg("[NOTE]\n");
+                Fw::Logger::logMsg("[NOTE] There are three possible resolutions:\n");
+                Fw::Logger::logMsg("[NOTE] 1. Use tasks without priority and affinity using parameterless start()\n");
+                Fw::Logger::logMsg("[NOTE] 2. Run this executable as a user with task priority permission\n");
+                Fw::Logger::logMsg("[NOTE] 3. Grant capability with \"setcap 'cap_sys_nice=eip'\" or equivalent\n");
+                Fw::Logger::logMsg("\n");
+                PosixTask::s_permissions_reported = true;
+            }
+            // Fallback with no permission
+            status = this->create(arguments, PermissionExpectation::EXPECT_NO_PERMISSION);
+        } else if (status != Os::Task::Status::OP_OK) {
+            Fw::Logger::logMsg("[ERROR] Failed to create task with status: %d",
+                               static_cast<PlatformPointerCastType>(static_cast<PlatformIntType>(status)));
         }
         return status;
     }
 
-    Task::TaskStatus Task::delay(NATIVE_UINT_TYPE milliseconds)
-    {
-        timespec time1;
-
-        time1.tv_sec = milliseconds/1000;
-        time1.tv_nsec = (milliseconds%1000)*1000000;
-
-        timespec time2;
-        time2.tv_sec = 0;
-        time2.tv_nsec = 0;
-
-        timespec* sleepTimePtr = &time1;
-        timespec* remTimePtr = &time2;
-
-        while (true) {
-            int stat = nanosleep(sleepTimePtr,remTimePtr);
-            if (0 == stat) {
-                return TASK_OK;
-            } else { // check errno
-                if (EINTR == errno) { // swap pointers
-                    timespec* temp = remTimePtr;
-                    remTimePtr = sleepTimePtr;
-                    sleepTimePtr = temp;
-                    continue; // if interrupted, just continue
-                } else {
-                    return TASK_DELAY_ERROR;
-                }
-            }
+    Os::Task::Status PosixTask::join() {
+        Os::Task::Status status = Os::Task::Status::JOIN_ERROR;
+        if (not this->m_handle.m_is_valid) {
+            status = Os::Task::Status::INVALID_HANDLE;
+        } else {
+            PlatformIntType stat = ::pthread_join(this->m_handle.m_task_descriptor, nullptr);
+            status = (stat == PosixTaskHandle::SUCCESS) ? Os::Task::Status::OP_OK : Os::Task::Status::JOIN_ERROR;
         }
-        return TASK_OK; // for coverage analysis
+        return status;
     }
 
-
-    Task::~Task() {
-    	if (this->m_handle) {
-    		delete reinterpret_cast<pthread_t*>(this->m_handle);
-    	}
-        // If a registry has been registered, remove task
-        if (Task::s_taskRegistry) {
-            Task::s_taskRegistry->removeTask(this);
-        }
-
+    TaskHandle* PosixTask::getHandle() {
+        return &this->m_handle;
     }
 
     // Note: not implemented for Posix threads. Must be manually done using a mutex or other blocking construct as there
-    // is not top-level pthreads support for suspend and resume.
-
-    void Task::suspend(bool onPurpose) {
-        FW_ASSERT(0);
-    }
-
-    void Task::resume() {
+    // is no top-level pthreads support for suspend and resume.
+    void PosixTask::suspend(Os::Task::SuspensionType suspensionType) {
         FW_ASSERT(0);
     }
 
-    bool Task::isSuspended() {
+    void PosixTask::resume() {
         FW_ASSERT(0);
-        return false;
-    }
-
-    TaskId Task::getOsIdentifier() {
-        TaskId T;
-        return T;
-    }
-
-    Task::TaskStatus Task::join(void **value_ptr) {
-        NATIVE_INT_TYPE stat = 0;
-        if (!(this->m_handle)) {
-            return TASK_JOIN_ERROR;
-        }
-        stat = pthread_join(*reinterpret_cast<pthread_t*>(this->m_handle), value_ptr);
-
-        if (stat != 0) {
-            return TASK_JOIN_ERROR;
-        }
-        else {
-            return TASK_OK;
-        }
     }
-}
+} // end namespace Task
+} // end namespace Posix
+} // end namespace Os
diff --git a/Os/Posix/Task.hpp b/Os/Posix/Task.hpp
new file mode 100644
index 0000000000..f040233574
--- /dev/null
+++ b/Os/Posix/Task.hpp
@@ -0,0 +1,114 @@
+// ======================================================================
+// \title Os/Posix/Task.hpp
+// \brief definitions of Posix implementation of Os::Task
+// ======================================================================
+#ifndef Os_Posix_Task_hpp_
+#define Os_Posix_Task_hpp_
+
+#include <atomic>
+#include <pthread.h>
+#include <Os/Task.hpp>
+
+#include <FpConfig.hpp>
+#include <Fw/Types/Serializable.hpp>
+#include <Os/TaskString.hpp>
+#include <Os/Mutex.hpp>
+#include <Fw/Deprecate.hpp>
+
+namespace Os {
+namespace Posix {
+namespace Task {
+
+    //! TaskHandle class definition for posix implementations.
+    //!
+    struct PosixTaskHandle : public TaskHandle {
+        static constexpr PlatformIntType SUCCESS = 0;
+
+        //! Posix task descriptor
+        pthread_t m_task_descriptor;
+        //! Is the above descriptor valid
+        bool m_is_valid = false;
+    };
+
+    //! Posix task implementation as driven by pthreads implementation
+    class PosixTask : public TaskInterface {
+      public:
+
+        //! Enumeration of permission expectations
+        enum PermissionExpectation {
+            EXPECT_PERMISSION, //!< Expect that you hold necessary permissions
+            EXPECT_NO_PERMISSION //!< Expect that you do not hold necessary permissions
+        };
+
+        //! \brief default constructor
+        PosixTask() = default;
+
+        //! \brief default virtual destructor
+        ~PosixTask() = default;
+
+        //! \brief copy constructor is forbidden
+        PosixTask(const PosixTask& other) = delete;
+
+        //! \brief assignment operator is forbidden
+        PosixTask& operator=(const PosixTask& other) = delete;
+
+
+        //! \brief perform required task start actions
+        void onStart() override;
+
+        //! \brief start the task
+        //!
+        //! Starts the task given the supplied arguments. This is done via the a task routine wrapper intermediary that
+        //! ensures that `setStarted` is called once the task has actually started to run. The task then runs the user
+        //! routine. This function may return before the new task begins to run.
+        //
+        //! It is illegal for arguments.m_routine to be null.
+        //!
+        //! \param arguments: arguments supplied to the task start call
+        //! \return status of the task start
+        Status start(const Arguments& arguments) override;
+
+        //! \brief block until the task has ended
+        //!
+        //! Blocks the current (calling) task until this task execution has ended. Callers should ensure that any
+        //! signals required to stop this task have already been emitted or will be emitted by another task.
+        //!
+        //! \return status of the block
+        Status join() override; //!< Wait for task to finish
+
+        //! \brief suspend the task given the suspension type
+        //!
+        //! Suspends the task. Some implementations track if the suspension of a task was intentional or
+        //! unintentional. The supplied `suspensionType` parameter indicates that this was intentional or
+        //! unintentional. The type of suspension is also returned when calling `isSuspended`.
+        //!
+        //! \param suspensionType intentionality of the suspension
+        void suspend(SuspensionType suspensionType) override;
+
+        //! \brief resume a suspended task
+        //!
+        //! Resumes this task. Not started, running, and exited tasks take no action.
+        //!
+        void resume() override;
+
+        //! \brief return the underlying task handle (implementation specific)
+        //! \return internal task handle representation
+        TaskHandle* getHandle() override;
+      PRIVATE:
+        //! \brief create a configured pthread
+        //!
+        //! Creates, and configures, but does not start a pthread. This may be called twice, once to try setting
+        //! permissions and once to fallback to no permissions.
+        //!
+        //! \param arguments: arguments used to set priority, affinity, etc
+        //! \param permissions: whether to expect permissions or not
+        //! \return OP_OK on success, or an error
+        Status create(const Os::Task::Arguments& arguments, const PosixTask::PermissionExpectation permissions);
+
+        PosixTaskHandle m_handle; //!< Posix task tracking
+        static std::atomic<bool> s_permissions_reported; //!< Permission errors have been reported
+    };
+} // end namespace Task
+} // end namespace Posix
+} // end namespace Os
+#endif
diff --git a/Os/Posix/TaskId.cpp b/Os/Posix/TaskId.cpp
deleted file mode 100644
index ad007c224e..0000000000
--- a/Os/Posix/TaskId.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-// File: TaskId.cpp
-// Author: Ben Soudry (benjamin.s.soudry@jpl.nasa.gov)
-//         Nathan Serafin (nathan.serafin@jpl.nasa.gov)
-// Date: 29 June, 2018
-//
-// POSIX implementation of TaskId type.
-
-extern "C" {
-#include <pthread.h>
-}
-
-#include <Os/TaskId.hpp>
-
-namespace Os
-{
-    TaskId::TaskId() : id(pthread_self())
-    {
-    }
-    TaskId::~TaskId()
-    {
-    }
-    bool TaskId::operator==(const TaskId& T) const
-    {
-        return pthread_equal(id, T.id);
-    }
-    bool TaskId::operator!=(const TaskId& T) const
-    {
-        return !pthread_equal(id, T.id);
-    }
-    TaskIdRepr TaskId::getRepr() const
-    {
-        return this->id;
-    }
-}
diff --git a/Os/Posix/errno.cpp b/Os/Posix/error.cpp
similarity index 67%
rename from Os/Posix/errno.cpp
rename to Os/Posix/error.cpp
index 6825168f25..741916c7b2 100644
--- a/Os/Posix/errno.cpp
+++ b/Os/Posix/error.cpp
@@ -1,9 +1,9 @@
 // ======================================================================
-// \title Os/Posix/errno.cpp
+// \title Os/Posix/error.cpp
 // \brief implementation for posix errno conversion
 // ======================================================================
 #include <cerrno>
-#include "Os/Posix/errno.hpp"
+#include "Os/Posix/error.hpp"
 
 namespace Os {
 namespace Posix {
@@ -48,5 +48,27 @@ File::Status errno_to_file_status(PlatformIntType errno_input) {
     return status;
 }
 
+Task::Status posix_status_to_task_status(PlatformIntType posix_status) {
+    Task::Status status = Task::Status::OP_OK;
+    switch (posix_status) {
+        case 0:
+            status = Task::Status::OP_OK;
+            break;
+        case EINVAL:
+            status = Task::Status::INVALID_PARAMS;
+            break;
+        case EPERM:
+            status = Task::Status::ERROR_PERMISSION;
+            break;
+        case EAGAIN:
+            status = Task::Status::ERROR_RESOURCES;
+            break;
+        default:
+            status = Task::Status::UNKNOWN_ERROR;
+            break;
+    }
+    return status;
+}
+
 }
 }
diff --git a/Os/Posix/errno.hpp b/Os/Posix/error.hpp
similarity index 57%
rename from Os/Posix/errno.hpp
rename to Os/Posix/error.hpp
index dd89888c32..b405d8e603 100644
--- a/Os/Posix/errno.hpp
+++ b/Os/Posix/error.hpp
@@ -1,8 +1,9 @@
 // ======================================================================
-// \title Os/Posix/errno.hpp
+// \title Os/Posix/error.hpp
 // \brief header for posix errno conversion
 // ======================================================================
 #include "Os/File.hpp"
+#include "Os/Task.hpp"
 #ifndef OS_POSIX_ERRNO_HPP
 #define OS_POSIX_ERRNO_HPP
 
@@ -10,11 +11,17 @@ namespace Os {
 namespace Posix {
 
 //! Convert an errno representation of an error to the Os::File::Status representation.
-//! \param errno: errno representation of the error
+//! \param errno_input: errno representation of the error
 //! \return: Os::File::Status representation of the error
 //!
 Os::File::Status errno_to_file_status(PlatformIntType errno_input);
 
+//! Convert an posix task representation of an error to the Os::Task::Status representation.
+//! \param posix_status: errno representation of the error
+//! \return: Os::Task::Status representation of the error
+//!
+Os::Task::Status posix_status_to_task_status(PlatformIntType posix_status);
+
 }
 }
 #endif
diff --git a/Os/Stub/CMakeLists.txt b/Os/Stub/CMakeLists.txt
index 7f62b620c6..589de397eb 100644
--- a/Os/Stub/CMakeLists.txt
+++ b/Os/Stub/CMakeLists.txt
@@ -18,6 +18,7 @@ if (NOT BUILD_TESTING)
     return()
 endif ()
 
+#### File Stub Testing ####
 set(SOURCE_FILES
     "${CMAKE_CURRENT_LIST_DIR}/test/File.cpp"
     "${CMAKE_CURRENT_LIST_DIR}/test/DefaultFile.cpp"
@@ -38,3 +39,26 @@ set(UT_MOD_DEPS
 )
 choose_fprime_implementation(Os/File Os_File_Test_Stub)
 register_fprime_ut(StubFileTest)
+
+
+#### Task Stub Testing ####
+set(SOURCE_FILES
+        "${CMAKE_CURRENT_LIST_DIR}/test/Task.cpp"
+        "${CMAKE_CURRENT_LIST_DIR}/test/DefaultTask.cpp"
+)
+register_fprime_module(Os_Task_Test_Stub)
+register_fprime_implementation(Os/Task Os_Task_Test_Stub)
+
+set(UT_SOURCE_FILES
+        "${CMAKE_CURRENT_LIST_DIR}/test/ut/StubTaskTests.cpp"
+        "${CMAKE_CURRENT_LIST_DIR}/../test/ut/task/CommonTests.cpp"
+        "${CMAKE_CURRENT_LIST_DIR}/../test/ut/task/TaskRules.cpp"
+)
+set(UT_MOD_DEPS
+        Fw/Time
+        Os
+        Os_Models
+        STest
+)
+choose_fprime_implementation(Os/Task Os_Task_Test_Stub)
+register_fprime_ut(StubTaskTest)
\ No newline at end of file
diff --git a/Os/Stub/DefaultFile.cpp b/Os/Stub/DefaultFile.cpp
index d5285656f2..07617a5d85 100644
--- a/Os/Stub/DefaultFile.cpp
+++ b/Os/Stub/DefaultFile.cpp
@@ -3,29 +3,16 @@
 // \brief sets default Os::File to no-op stub implementation via linker
 // ======================================================================
 #include "Os/Stub/File.hpp"
-#include "Fw/Types/Assert.hpp"
-#include <new>
+#include "Os/Delegate.hpp"
 namespace Os {
-//! \brief get implementation file interface for use as delegate
-//!
-//! Added via linker to ensure that the no-op stub implementation of Os::File is the default.
-//!
-//! \param aligned_placement_new_memory: memory to placement-new into
-//! \return FileInterface pointer result of placement new
-//!
-FileInterface *FileInterface::getDelegate(U8 *aligned_placement_new_memory, const FileInterface* to_copy) {
-    FW_ASSERT(aligned_placement_new_memory != nullptr);
-    const Os::Stub::File::StubFile* copy_me = reinterpret_cast<const Os::Stub::File::StubFile*>(to_copy);
-    // Placement-new the file handle into the opaque file-handle storage
-    static_assert(sizeof(Os::Stub::File::StubFile) <= sizeof Os::File::m_handle_storage, "Handle size not large enough");
-    static_assert((FW_HANDLE_ALIGNMENT % alignof(Os::Stub::File::StubFile)) == 0, "Handle alignment invalid");
-    Os::Stub::File::StubFile *interface = nullptr;
-    if (to_copy == nullptr) {
-        interface = new(aligned_placement_new_memory) Os::Stub::File::StubFile;
-    } else {
-        interface = new(aligned_placement_new_memory) Os::Stub::File::StubFile(*copy_me);
-    }
-    FW_ASSERT(interface != nullptr);
-    return interface;
+
+//! \brief get a delegate for FileInterface that intercepts calls for stub file usage
+//! \param aligned_new_memory: aligned memory to fill
+//! \param to_copy: pointer to copy-constructor input
+//! \return: pointer to delegate
+FileInterface *FileInterface::getDelegate(HandleStorage& aligned_placement_new_memory, const FileInterface* to_copy) {
+    return Os::Delegate::makeDelegate<FileInterface, Os::Stub::File::StubFile>(
+            aligned_placement_new_memory, to_copy
+    );
 }
 }
diff --git a/Os/Task.cpp b/Os/Task.cpp
new file mode 100644
index 0000000000..d7f3ae5587
--- /dev/null
+++ b/Os/Task.cpp
@@ -0,0 +1,180 @@
+// ======================================================================
+// \title Os/Task.cpp
+// \brief common function implementation for Os::Task
+// ======================================================================
+#include <Os/Task.hpp>
+#include <Fw/Types/Assert.hpp>
+
+namespace Os {
+
+TaskInterface::Arguments::Arguments(const Fw::StringBase &name, const Os::TaskInterface::taskRoutine routine,
+                                    void * const routine_argument, const FwSizeType priority,
+                                    const FwSizeType stackSize, const FwSizeType cpuAffinity,
+                                    const PlatformUIntType identifier) :
+    m_name(name),
+    m_routine(routine),
+    m_routine_argument(routine_argument),
+    m_priority(priority),
+    m_stackSize(stackSize),
+    m_cpuAffinity(cpuAffinity),
+    m_identifier(identifier)
+{
+    FW_ASSERT(routine != nullptr);
+}
+
+Task::TaskRoutineWrapper::TaskRoutineWrapper(Task& self) : m_task(self) {}
+
+void Task::TaskRoutineWrapper::run(void* wrapper_pointer) {
+    FW_ASSERT(wrapper_pointer != nullptr);
+    TaskRoutineWrapper& wrapper = *reinterpret_cast<TaskRoutineWrapper*>(wrapper_pointer);
+    FW_ASSERT(wrapper.m_user_function != nullptr);
+
+    wrapper.m_task.m_lock.lock();
+    Task::State state = wrapper.m_task.m_state;
+    wrapper.m_task.m_lock.unlock();
+
+    // Run once start code
+    if (state == Task::State::NOT_STARTED) {
+        wrapper.m_task.m_lock.lock();
+        wrapper.m_task.m_state = Task::State::RUNNING;
+        wrapper.m_task.m_lock.unlock();
+        wrapper.m_task.onStart();
+    }
+
+    // Call user function supplying the user argument
+    wrapper.m_user_function(wrapper.m_user_argument);
+}
+
+void Task::TaskRoutineWrapper::invoke() {
+    TaskRoutineWrapper::run(this);
+}
+
+TaskRegistry* Task::s_taskRegistry = nullptr;
+FwSizeType Task::s_numTasks = 0;
+Mutex Task::s_taskMutex = Mutex();
+
+bool TaskInterface::isCooperative() {
+    return false;
+}
+
+Task::Task() : m_wrapper(*this), m_handle_storage(), m_delegate(*TaskInterface::getDelegate(m_handle_storage)) {}
+
+Task::~Task() {
+    // If a registry has been registered and the task has been started then remove task from the registry
+    if ((Task::s_taskRegistry != nullptr) && this->m_registered) {
+        Task::s_taskRegistry->removeTask(this);
+    }
+    m_delegate.~TaskInterface();
+}
+
+void Task::suspend() {
+    this->suspend(Task::SuspensionType::UNINTENTIONAL);
+}
+
+Task::State Task::getState() {
+    Task::State state;
+    this->m_lock.lock();
+    state = this->m_state;
+    this->m_lock.unlock();
+    return state;
+}
+
+Task::Status Task::start(const Fw::StringBase &name, const taskRoutine routine, void* const arg,
+                         const ParamType priority, const ParamType stackSize, const ParamType cpuAffinity,
+                         const ParamType identifier) {
+    FW_ASSERT(routine != nullptr);
+    return this->start(Task::Arguments(name, routine, arg,
+                                       priority,
+                                       stackSize,
+                                       cpuAffinity,
+                                       static_cast<PlatformUIntType>(identifier)));
+}
+
+
+Task::Status Task::start(const Task::Arguments& arguments) {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
+    FW_ASSERT(arguments.m_routine != nullptr);
+    this->m_name = arguments.m_name;
+
+    Arguments wrapped_arguments = arguments;
+    // Intercept routine and argument with the local wrapper
+    this->m_wrapper.m_user_function = arguments.m_routine;
+    this->m_wrapper.m_user_argument = arguments.m_routine_argument;
+    wrapped_arguments.m_routine = Task::TaskRoutineWrapper::run;
+    wrapped_arguments.m_routine_argument = &this->m_wrapper;
+
+    Task::Status status = this->m_delegate.start(wrapped_arguments);
+    if (status == Task::Status::OP_OK) {
+        Task::s_taskMutex.lock();
+        Task::s_numTasks++;
+        Task::s_taskMutex.unlock();
+
+        // If a registry has been registered, register task to it
+        if (Task::s_taskRegistry) {
+            Task::s_taskRegistry->addTask(this);
+            this->m_registered = true;
+        }
+    }
+    return status;
+}
+
+void Task::onStart() {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
+    this->m_delegate.onStart();
+}
+
+void Task::invokeRoutine() {
+    this->m_wrapper.invoke();
+}
+
+Task::Status Task::join() {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
+    Task::Status status = Task::Status::INVALID_STATE;
+    Task::State state = this->getState();
+    if (state == Task::RUNNING) {
+        status = this->m_delegate.join();
+        this->m_lock.lock();
+        if (status == Task::Status::OP_OK) {
+            this->m_state = Task::State::EXITED;
+        } else {
+            this->m_state = Task::State::UNKNOWN;
+        }
+        this->m_lock.unlock();
+    }
+    return status;
+}
+
+void Task::suspend(Task::SuspensionType suspensionType) {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
+    this->m_delegate.suspend(suspensionType);
+    this->m_lock.lock();
+    this->m_state = (suspensionType == Task::SuspensionType::INTENTIONAL) ? State::SUSPENDED_INTENTIONALLY : State::SUSPENDED_UNINTENTIONALLY;
+    this->m_lock.unlock();
+}
+
+void Task::resume() {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
+    this->m_delegate.resume();
+}
+
+bool Task::isCooperative() {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
+    return this->m_delegate.isCooperative();
+}
+
+TaskHandle* Task::getHandle() {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
+    return this->m_delegate.getHandle();
+}
+
+FwSizeType Task::getNumTasks() {
+    Task::s_taskMutex.lock();
+    FwSizeType num_tasks = Task::s_numTasks;
+    Task::s_taskMutex.unlock();
+    return num_tasks;
+}
+
+void Task::registerTaskRegistry(TaskRegistry* registry) {
+    Task::s_taskRegistry = registry;
+}
+}
diff --git a/Os/Task.hpp b/Os/Task.hpp
index be5e40fcf9..1c88779858 100644
--- a/Os/Task.hpp
+++ b/Os/Task.hpp
@@ -1,93 +1,373 @@
-#ifndef _Task_hpp_
-#define _Task_hpp_
+// ======================================================================
+// \title Os/Task.hpp
+// \brief common function definitions for Os::Task
+// ======================================================================
+#ifndef Os_Task_hpp_
+#define Os_Task_hpp_
 
 #include <FpConfig.hpp>
+#include <Fw/Time/Time.hpp>
 #include <Fw/Types/Serializable.hpp>
+#include <Os/Os.hpp>
 #include <Os/TaskString.hpp>
+#include <Os/Mutex.hpp>
 
 #include <Fw/Deprecate.hpp>
-#include <Os/TaskId.hpp>
 #include <limits>
 
 namespace Os {
 
-class TaskRegistry;  //!< forward declaration
-class Task {
-  public:
-    using ParamType = NATIVE_UINT_TYPE;
-    static const ParamType TASK_DEFAULT;
-    typedef enum {
-        TASK_OK,                //!< message sent/received okay
-        TASK_INVALID_PARAMS,    //!< started task with invalid parameters
-        TASK_INVALID_STACK,     //!< started with invalid stack size
-        TASK_UNKNOWN_ERROR,     //!< unexpected error return value
-        TASK_INVALID_AFFINITY,  //!< unable to set the task affinity
-        TASK_DELAY_ERROR,       //!< error trying to delay the task
-        TASK_JOIN_ERROR,        //!< error trying to join the task
-        TASK_ERROR_RESOURCES,   //!< unable to allocate more tasks
-        TASK_ERROR_PERMISSION,  //!< permissions error setting-up tasks
-    } TaskStatus;
-
-    typedef void (*taskRoutine)(void* ptr);  //!< prototype for task routine started in task context
-
-    struct TaskRoutineWrapper {
-        taskRoutine routine;  //!< contains the task entrypoint
-        void* arg;            //!< contains the task entrypoint pointer
+    // Forward declarations
+    class TaskRegistry;
+
+    //! Task handle representation
+    class TaskHandle {};
+
+    class TaskInterface {
+        public:
+        static constexpr FwSizeType TASK_DEFAULT = std::numeric_limits<FwSizeType>::max();
+            enum Status {
+                OP_OK, //!< message sent/received okay
+                INVALID_HANDLE, //!< Task handle invalid
+                INVALID_PARAMS, //!< started task with invalid parameters
+                INVALID_STACK, //!< started with invalid stack size
+                UNKNOWN_ERROR, //!< unexpected error return value
+                INVALID_AFFINITY, //!< unable to set the task affinity
+                DELAY_ERROR, //!< error trying to delay the task
+                JOIN_ERROR, //!< error trying to join the task
+                ERROR_RESOURCES, //!< unable to allocate more tasks
+                ERROR_PERMISSION, //!< permissions error setting-up tasks
+                INVALID_STATE, //!< Task is in an invalid state for the operation
+            };
+
+            enum SuspensionType {
+                INTENTIONAL,
+                UNINTENTIONAL
+            };
+
+            enum State {
+                NOT_STARTED,
+                RUNNING,
+                SUSPENDED_INTENTIONALLY,
+                SUSPENDED_UNINTENTIONALLY,
+                EXITED,
+                UNKNOWN
+            };
+
+            //! Prototype for task routine started in task context
+            typedef void (*taskRoutine)(void* ptr);
+
+            class Arguments {
+              public:
+                //! \brief construct a set of arguments to start a task
+                //!
+                //! Construct a set of arguments to start a task. It is illegal to supply a task routine that is
+                //! set to a nullptr.
+                //!
+                //! \param name: name of the task
+                //! \param routine: routine to run as part of this task
+                //! \param routine_argument: (optional) argument to supply to the task routine
+                //! \param priority: (optional) priority of this task
+                //! \param stackSize: (optional) size of stack supplied to this task
+                //! \param cpuAffinity: (optional) cpu affinity of this task. TODO: fix this into an array
+                //! \param identifier: (optional) identifier for this task
+                Arguments(const Fw::StringBase& name, const taskRoutine routine,
+                          void* const routine_argument = nullptr,
+                          const FwSizeType priority = TASK_DEFAULT,
+                          const FwSizeType stackSize = TASK_DEFAULT,
+                          const FwSizeType cpuAffinity = TASK_DEFAULT,
+                          const PlatformUIntType identifier = static_cast<PlatformUIntType>(TASK_DEFAULT));
+
+              public:
+                const Os::TaskString m_name;
+                taskRoutine m_routine;
+                void* m_routine_argument;
+                FwSizeType m_priority;
+                FwSizeType m_stackSize;
+                FwSizeType m_cpuAffinity;
+                PlatformUIntType m_identifier;
+            };
+
+            //! \brief default constructor
+            TaskInterface() = default;
+
+            //! \brief default virtual destructor
+            virtual ~TaskInterface() = default;
+
+            //! \brief copy constructor is forbidden
+            TaskInterface(const TaskInterface& other) = delete;
+
+            //! \brief assignment operator is forbidden
+            TaskInterface& operator=(const TaskInterface& other) = delete;
+
+            // =================
+            // Implementation functions (static) to be supplied by the linker
+            // =================
+
+            //! \brief delay the current task
+            //!
+            //! Delays, or sleeps, the current task by the supplied time interval. In non-preempting os implementations
+            //! the task will resume no earlier than expected but an exact wake-up time is not guaranteed.
+            //!
+            //! \param interval: delay time
+            //! \return status of the delay
+            static Status delay(Fw::Time interval);
+
+            //! \brief provide a pointer to a task delegate object
+            //!
+            //! This function must return a pointer to a `TaskInterface` object that contains the real implementation of the file
+            //! functions as defined by the implementor.  This function must do several things to be considered correctly
+            //! implemented:
+            //!
+            //! 1. Assert that the supplied memory is non-null. e.g `FW_ASSERT(aligned_placement_new_memory != NULL);`
+            //! 2. Assert that their implementation fits within FW_HANDLE_MAX_SIZE.
+            //!    e.g. `static_assert(sizeof(PosixTaskImplementation) <= sizeof Os::Task::m_handle_storage,
+            //!        "FW_HANDLE_MAX_SIZE to small");`
+            //! 3. Assert that their implementation aligns within FW_HANDLE_ALIGNMENT.
+            //!    e.g. `static_assert((FW_HANDLE_ALIGNMENT % alignof(PosixTaskImplementation)) == 0, "Bad handle alignment");`
+            //! 4. Placement new their implementation into `aligned_placement_new_memory`
+            //!    e.g. `TaskInterface* interface = new (aligned_placement_new_memory) PosixTaskImplementation;`
+            //! 5. Return the result of the placement new
+            //!    e.g. `return interface;`
+            //!
+            //! \return result of placement new, must be equivalent to `aligned_placement_new_memory`
+            //!
+            static TaskInterface* getDelegate(HandleStorage& aligned_placement_new_memory);
+
+            // =================
+            // Implementation functions (instance) to be supplied by the Os::TaskInterface children
+            // =================
+
+            //! \brief perform required task start actions
+            virtual void onStart() = 0;
+
+            //! \brief block until the task has ended
+            //!
+            //! Blocks the current (calling) task until this task execution has ended. Callers should ensure that any
+            //! signals required to stop this task have already been emitted or will be emitted by another task.
+            //!
+            //! \return status of the block
+            virtual Status join() = 0;
+
+            //! \brief suspend the task given the suspension type
+            //!
+            //! Suspends the task. Some implementations track if the suspension of a task was intentional or
+            //! unintentional. The supplied `suspensionType` parameter indicates that this was intentional or
+            //! unintentional. The type of suspension is also returned when calling `isSuspended`.
+            //!
+            //! \param suspensionType intentionality of the suspension
+            virtual void suspend(SuspensionType suspensionType) = 0;
+
+            //! \brief resume a suspended task
+            //!
+            //! Resumes this task. Not started, running, and exited tasks take no action.
+            //!
+            virtual void resume() = 0;
+
+            //! \brief determine if the task requires cooperative multitasking
+            //!
+            //! Some task implementations require cooperative multitasking where the task execution is run by a user
+            //! defined task scheduler and not the operating system task scheduler. These tasks cooperatively on
+            //! multitask by doing one unit of work and return from the function.
+            //!
+            //! This function indicates if the task requires cooperative support.
+            //! The default implementation returns false.
+            //!
+            //! \return true when the task expects cooperation, false otherwise
+            virtual bool isCooperative();
+
+            //! \brief return the underlying task handle (implementation specific)
+            //! \return internal task handle representation
+            virtual TaskHandle* getHandle() = 0;
+
+            //! \brief start the task
+            //!
+            //! Starts the task given the supplied arguments.
+            //!
+            //! \param arguments: arguments supplied to the task start call
+            //! \return status of the task start
+            virtual Status start(const Arguments& arguments) = 0;
+    };
+
+    //! Task class intended to be used by the rest of the fprime system. This is final as it is not intended to be a
+    //! parent class. Instead it wraps a delegate provided by `TaskInterface::getDelegate()` to provide system specific
+    //! behaviour.
+    class Task final : public TaskInterface {
+      public:
+        //! Wrapper for task routine that ensures `onStart()` is called once the task actually begins
+        class TaskRoutineWrapper {
+          public:
+            explicit TaskRoutineWrapper(Task& self);
+
+            //! \brief run the task routine wrapper
+            //!
+            //! Sets the Os::Task to started via the setStarted method. Then runs the user function passing in the
+            //! user argument.
+            //! \param task_pointer: pointer to TaskRoutineWrapper being run
+            static void run(void* task_pointer);
+
+            //! \brief invoke the run method with "self" as argument
+            void invoke();
+
+            Task& m_task; //!< Reference to owning task
+            taskRoutine m_user_function = nullptr; //!< User function to run once started
+            void* m_user_argument = nullptr; //!<  Argument to user function
+        };
+
+        //! \brief backwards-compatible parameter type
+        typedef FwSizeType ParamType;
+
+        //! \brief default constructor
+        Task();
+
+        //! \brief default virtual destructor
+        ~Task() final;
+
+        //! \brief copy constructor is forbidden
+        Task(const Task& other) = delete;
+
+        //! \brief assignment operator is forbidden
+        Task& operator=(const Task& other) = delete;
+
+        //! \brief suspend the current task
+        //!
+        //! Suspend the current task unintentionally. If the user needs to indicate that the task was suspended
+        //! intentionally then a call to `suspend(SuspensionType::INTENTIONAL)` should be used.
+        void suspend();
+
+        //! \brief get the task's state
+        //!
+        //! Returns the task state: not started, running, suspended (intentionally), suspended (unintentionally),
+        //! and exited.
+        //!
+        //! \return task state
+        State getState();
+
+        //! \brief start this task
+        //!
+        //! Start this task with supplied name, task routine (run function), priority, stack, affinity, and task
+        //! identifier. These arguments are supplied into an Arguments class and that version of the function is called.
+        //! It is illegal to supply a nullptr as routine.
+        //!
+        //! \param name: name of the task to start
+        //! \param routine: user routine to run
+        //! \param arg: (optional) user argument to supply to task routine
+        //! \param priority: (optional) priority of this task
+        //! \param stackSize: (optional) stack size of this task
+        //! \param cpuAffinity: (optional) affinity of this task. Use `Task::start(Arguments&)` to supply affinity set.
+        //! \param identifier: (optional) identifier of this task
+        //! \return: status of the start call
+        DEPRECATED(Status start(const Fw::StringBase &name, const taskRoutine routine, void* const arg = nullptr,
+                                const ParamType priority = TASK_DEFAULT,
+                                const ParamType stackSize = TASK_DEFAULT,
+                                const ParamType cpuAffinity = TASK_DEFAULT,
+                                const ParamType identifier = TASK_DEFAULT), "Switch to Task::start(Arguments&)");
+
+        //! \brief start the task
+        //!
+        //! Starts the task given the supplied arguments. This is done via the a task routine wrapper intermediary that
+        //! ensures that `setStarted` is called once the task has actually started to run. The task then runs the user
+        //! routine. This function may return before the new task begins to run.
+        //
+        //! It is illegal for arguments.m_routine to be null.
+        //!
+        //! \param arguments: arguments supplied to the task start call
+        //! \return status of the task start
+        Status start(const Arguments& arguments) override;
+
+        //! \brief perform delegate's required task start actions
+        void onStart() override;
+
+        //! \brief invoke the task's routine
+        //~
+        //! This will invoke the task's routine passing this as the argument to that call. This is used as a helper when
+        //! running this task (e.g. repetitive cooperative calls).
+        void invokeRoutine();
+
+        //! \brief join calling thread to this thread
+        //!
+        //! Note: this function is deprecated as the value_ptr object is not used anyway and should always be set
+        //! to nullptr.
+        //!
+        //! \param value_ptr must be set to nullptr
+        //! \return status of the join
+        DEPRECATED(Status join(void** value_ptr), "Please switch to argument free join.");
+
+        //! \brief block until the task has ended
+        //!
+        //! Blocks the current (calling) task until this task execution has ended. Callers should ensure that any
+        //! signals required to stop this task have already been emitted or will be emitted by another task.
+        //!
+        //! \return status of the block
+        Status join() override; //!< Wait for task to finish
+
+        //! \brief suspend the task given the suspension type
+        //!
+        //! Suspends the task. Some implementations track if the suspension of a task was intentional or
+        //! unintentional. The supplied `suspensionType` parameter indicates that this was intentional or
+        //! unintentional. The type of suspension is also returned when calling `isSuspended`.
+        //!
+        //! \param suspensionType intentionality of the suspension
+        void suspend(SuspensionType suspensionType) override;
+
+        //! \brief resume a suspended task
+        //!
+        //! Resumes this task. Not started, running, and exited tasks take no action.
+        //!
+        void resume() override;
+
+        //! \brief determine if the task is cooperative multitasking (implementation specific)
+        //! \return true if cooperative, false otherwise
+        bool isCooperative() override;
+
+        //! \brief return the underlying task handle (implementation specific)
+        //! \return internal task handle representation
+        TaskHandle* getHandle() override;
+
+        //! \brief get the current number of tasks
+        //! \return current number of tasks
+        static FwSizeType getNumTasks();
+
+        //! \brief register a task registry to track Threads
+        //!
+        static void registerTaskRegistry(TaskRegistry* registry);
+
+      PRIVATE:
+        static TaskRegistry* s_taskRegistry; //!< Pointer to registered task registry
+        static FwSizeType s_numTasks; //!< Stores the number of tasks created.
+        static Mutex s_taskMutex; //!< Guards s_numTasks
+
+        TaskString m_name; //!< Task object name
+        TaskInterface::State m_state = Task::NOT_STARTED;
+        Mutex m_lock; //!< Guards state transitions
+        TaskRoutineWrapper m_wrapper; //!< Concrete storage for task routine wrapper
+
+        bool m_registered = false; //!< Was this task registered
+
+        // This section is used to store the implementation-defined file handle. To Os::File and fprime, this type is
+        // opaque and thus normal allocation cannot be done. Instead, we allow the implementor to store then handle in
+        // the byte-array here and set `handle` to that address for storage.
+        //
+        alignas(FW_HANDLE_ALIGNMENT) HandleStorage m_handle_storage; //!< Storage for aligned FileHandle data
+        TaskInterface& m_delegate; //!< Delegate for the real implementation
     };
 
-    Task();           //!< constructor
-    virtual ~Task();  //!< destructor
-
-    TaskStatus start(const Fw::StringBase& name,
-                     taskRoutine routine,
-                     void* arg,
-                     ParamType priority = TASK_DEFAULT,
-                     ParamType stackSize = TASK_DEFAULT,
-                     ParamType cpuAffinity = TASK_DEFAULT,
-                     ParamType identifier = TASK_DEFAULT);  //!< start the task
-
-    I32 getIdentifier();              //!< get the identifier for the task
-    static TaskId getOsIdentifier();  // Gets the Os Task ID. Useful for passive components.
-
-    static TaskStatus delay(ParamType msecs);  //!< delay the task
-    static NATIVE_INT_TYPE getNumTasks();
-
-    TaskStatus join(void** value_ptr);     //!< Wait for task to finish
-    void suspend(bool onPurpose = false);  //!< suspend task
-    void resume();                         //!< resume execution of task
-    bool wasSuspended();                   //!< returns whether or not task was suspended on purpose
-    bool isSuspended();                    //!< check with OS to see if it is suspended already
-    bool isStarted();                      //!< check to see if task is started
-    void setStarted(bool started);  //!< set task to started when thread is fully up. Avoids a VxWorks race condition.
-    /**
-     * Returns the task-handle owned by this task
-     */
-    POINTER_CAST getRawHandle();
-
-    static void registerTaskRegistry(TaskRegistry* registry);
-
-  private:
-    POINTER_CAST m_handle;         //!< handle for implementation specific task
-    NATIVE_INT_TYPE m_identifier;  //!< thread independent identifier
-    TaskString m_name;             //!< object name
-    NATIVE_INT_TYPE m_affinity;    //!< CPU affinity for SMP targets
-
-    void toString(char* buf, NATIVE_INT_TYPE buffSize);  //!< print a string of the state of the task
-    bool m_started;                                      //!< set when task has reached entry point
-    bool m_suspendedOnPurpose;                           //!< set when task was suspended in purpose (i.e. simulation)
-    TaskRoutineWrapper m_routineWrapper;                 //! Contains task entrypoint and argument for task wrapper
-
-    static TaskRegistry* s_taskRegistry;  //!< pointer to registered task
-    static NATIVE_INT_TYPE s_numTasks;    //!< stores the number of tasks created.
-};
-
-class TaskRegistry {
-  public:
-    TaskRegistry();                           //!< constructor for task registry
-    virtual ~TaskRegistry();                  //!< destructor for task registry
-    virtual void addTask(Task* task) = 0;     //!< Add a task to the registry
-    virtual void removeTask(Task* task) = 0;  //!< remove a task from the registry
-};
-}  // namespace Os
+    class TaskRegistry {
+        public:
+            //! \brief default task registry constructor
+            TaskRegistry() = default;
+            //! \brief default task registry constructor
+            virtual ~TaskRegistry() = default;
+            //! \brief add supplied task to the registry
+            //!
+            //! \param task: pointer to task to register
+            virtual void addTask(Task* task) = 0; //!< Add a task to the registry
+
+            //! \brief remove supplied task to the registry
+            //!
+            //! \param task: pointer to task to deregister
+            virtual void removeTask(Task* task) = 0;
+    };
+}
 
 #endif
diff --git a/Os/TaskCommon.cpp b/Os/TaskCommon.cpp
deleted file mode 100644
index 94f9cd9c2c..0000000000
--- a/Os/TaskCommon.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#include <Os/Task.hpp>
-#include <Fw/Types/Assert.hpp>
-#include <FpConfig.hpp>
-
-
-namespace Os {
-    const Task::ParamType Task::TASK_DEFAULT = std::numeric_limits<Task::ParamType>::max();
-    
-    TaskRegistry* Task::s_taskRegistry = nullptr;
-    NATIVE_INT_TYPE Task::s_numTasks = 0;
-
-    NATIVE_INT_TYPE Task::getNumTasks() {
-        return Task::s_numTasks;
-    }
-
-
-    I32 Task::getIdentifier() {
-        return m_identifier;
-    }
-
-    bool Task::isStarted() {
-        return this->m_started;
-    }
-
-    void Task::setStarted(bool started) {
-        this->m_started = started;
-    }
-
-    bool Task::wasSuspended() {
-        return this->m_suspendedOnPurpose;
-    }
-
-    POINTER_CAST Task::getRawHandle() {
-        return this->m_handle;
-    }
-
-    void Task::registerTaskRegistry(TaskRegistry* registry) {
-        FW_ASSERT(registry);
-        Task::s_taskRegistry = registry;
-    }
-
-    TaskRegistry::TaskRegistry() {
-
-    }
-
-    TaskRegistry::~TaskRegistry() {
-
-    }
-
-}
diff --git a/Os/TaskId.hpp b/Os/TaskId.hpp
deleted file mode 100644
index 15c0069e74..0000000000
--- a/Os/TaskId.hpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// File: TaskId.hpp
-// Author: Ben Soudry (benjamin.s.soudry@jpl.nasa.gov)
-//         Nathan Serafin (nathan.serafin@jpl.nasa.gov)
-// Date: 29 June, 2018
-//
-// Define a type for task IDs.  This is useful as POSIX only
-// provides an opaque TID with a special pthread_equal() comparison
-// function.  For higher-level code to not need to be aware of
-// POSIX versus VxWorks versus whatever else, we can overload the
-// == operator to perform the correct equality check.
-
-#ifndef _TaskId_hpp_
-#define _TaskId_hpp_
-
-#include <Os/TaskIdRepr.hpp>
-
-namespace Os {
-  class TaskId {
-    public:
-        TaskId();
-        ~TaskId();
-        bool operator==(const TaskId& T) const;
-        bool operator!=(const TaskId& T) const;
-        TaskIdRepr getRepr() const;
-    private:
-        TaskIdRepr id;
-  };
-}
-
-#endif
diff --git a/Os/TaskIdRepr.hpp b/Os/TaskIdRepr.hpp
deleted file mode 100644
index 33bc3d23bf..0000000000
--- a/Os/TaskIdRepr.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// File: TaskIdRepr.hpp
-// Author: Nathan Serafin (nathan.serafin@jpl.nasa.gov)
-// Date: 29 June, 2018
-//
-// Depending on the target operating system, define a type
-// for the storage of task IDs.
-
-#ifndef TASKIDREPR_HPP
-#define TASKIDREPR_HPP
-
-#if defined(TGT_OS_TYPE_LINUX) || defined(TGT_OS_TYPE_DARWIN)
-extern "C" {
-#include <pthread.h>
-}
-#endif
-
-namespace Os {
-#if defined(TGT_OS_TYPE_VXWORKS) || (FW_BAREMETAL_SCHEDULER == 1)
-    typedef int TaskIdRepr;
-#elif defined(TGT_OS_TYPE_LINUX) || defined(TGT_OS_TYPE_DARWIN)
-    typedef pthread_t TaskIdRepr;
-#endif
-}
-
-#endif
diff --git a/RPI/Main.cpp b/RPI/Main.cpp
index d3c634c1c8..ab032f7d08 100644
--- a/RPI/Main.cpp
+++ b/RPI/Main.cpp
@@ -3,7 +3,7 @@
 #include <ctype.h>
 #include <getopt.h>
 #include <signal.h>
-
+#include <Fw/Time/Time.hpp>
 #include <RPI/Top/RPITopologyAc.hpp>
 
 RPI::TopologyState state;
@@ -61,7 +61,7 @@ int main(int argc, char* argv[]) {
     // Time to exit the program.
     // Give time for threads to exit.
     (void) printf("Waiting for threads...\n");
-    Os::Task::delay(1000);
+    Os::Task::delay(Fw::Time(1, 0));
 
     (void) printf("Exiting...\n");
 
diff --git a/RPI/Top/instances.fpp b/RPI/Top/instances.fpp
index 055215cb6c..b162f33687 100644
--- a/RPI/Top/instances.fpp
+++ b/RPI/Top/instances.fpp
@@ -258,7 +258,7 @@ module RPI {
         Os::TaskString name("ReceiveTask");
         // Uplink is configured for receive so a socket task is started
         comm.configure(state.hostName, state.portNumber);
-        comm.startSocketTask(
+        comm.start(
             name,
             ConfigConstants::comm::PRIORITY,
             ConfigConstants::comm::STACK_SIZE
@@ -267,11 +267,11 @@ module RPI {
     """
 
     phase Fpp.ToCpp.Phases.stopTasks """
-    comm.stopSocketTask();
+    comm.stop();
     """
 
     phase Fpp.ToCpp.Phases.freeThreads """
-    (void) comm.joinSocketTask(nullptr);
+    (void) comm.join();
     """
 
   }
@@ -323,7 +323,7 @@ module RPI {
 
     phase Fpp.ToCpp.Phases.startTasks """
     if (Init::status) {
-      uartDrv.startReadThread();
+      uartDrv.start();
     }
     else {
       Fw::Logger::logMsg("[ERROR] Initialization failed; not starting UART driver\\n");
diff --git a/Ref/Main.cpp b/Ref/Main.cpp
index 568ff89682..cff59cf6c9 100644
--- a/Ref/Main.cpp
+++ b/Ref/Main.cpp
@@ -88,7 +88,7 @@ int main(int argc, char* argv[]) {
 
     // Setup, cycle, and teardown topology
     Ref::setupTopology(inputs);
-    Ref::startSimulatedCycle(1000);  // Program loop cycling rate groups at 1Hz
+    Ref::startSimulatedCycle(Fw::Time(1, 0));  // Program loop cycling rate groups at 1Hz
     Ref::teardownTopology(inputs);
     (void)printf("Exiting...\n");
     return 0;
diff --git a/Ref/Top/RefTopology.cpp b/Ref/Top/RefTopology.cpp
index 3627ca9b15..7ad61e0ef0 100644
--- a/Ref/Top/RefTopology.cpp
+++ b/Ref/Top/RefTopology.cpp
@@ -161,7 +161,7 @@ void setupTopology(const TopologyState& state) {
         Os::TaskString name("ReceiveTask");
         // Uplink is configured for receive so a socket task is started
         comm.configure(state.hostname, state.port);
-        comm.startSocketTask(name, true, COMM_PRIORITY, Default::STACK_SIZE);
+        comm.start(name, true, COMM_PRIORITY, Default::STACK_SIZE);
     }
 }
 
@@ -169,7 +169,7 @@ void setupTopology(const TopologyState& state) {
 Os::Mutex cycleLock;
 volatile bool cycleFlag = true;
 
-void startSimulatedCycle(U32 milliseconds) {
+void startSimulatedCycle(Fw::Time interval) {
     cycleLock.lock();
     bool cycling = cycleFlag;
     cycleLock.unLock();
@@ -177,7 +177,7 @@ void startSimulatedCycle(U32 milliseconds) {
     // Main loop
     while (cycling) {
         Ref::blockDrv.callIsr();
-        Os::Task::delay(milliseconds);
+        Os::Task::delay(interval);
 
         cycleLock.lock();
         cycling = cycleFlag;
@@ -197,8 +197,8 @@ void teardownTopology(const TopologyState& state) {
     freeThreads(state);
 
     // Other task clean-up.
-    comm.stopSocketTask();
-    (void)comm.joinSocketTask(nullptr);
+    comm.stop();
+    (void)comm.join();
 
     // Resource deallocation
     cmdSeq.deallocateBuffer(mallocator);
diff --git a/Ref/Top/RefTopology.hpp b/Ref/Top/RefTopology.hpp
index b91e477f5f..7c7d676348 100644
--- a/Ref/Top/RefTopology.hpp
+++ b/Ref/Top/RefTopology.hpp
@@ -69,16 +69,15 @@ void teardownTopology(const TopologyState& state);
  *
  * The reference topology does not have a true 1Hz input clock for the rate group driver because it is designed to
  * operate across various computing endpoints (e.g. laptops) where a clear 1Hz source may not be easily and generically
- * achieved. This function mimics the cycling via a Task::delay(milliseconds) loop that manually invokes the ISR call
+ * achieved. This function mimics the cycling via a Task::delay(Fw::Time()) loop that manually invokes the ISR call
  * to the example block driver.
  *
  * This loop is stopped via a startSimulatedCycle call.
  *
  * Note: projects should replace this with a component that produces an output port call at the appropriate frequency.
  *
- * \param milliseconds: milliseconds to delay for each cycle. Default: 1000 or 1Hz.
  */
-void startSimulatedCycle(U32 milliseconds = 1000);
+void startSimulatedCycle(Fw::Time interval);
 
 /**
  * \brief stop the simulated cycle started by startSimulatedCycle
diff --git a/Svc/FatalHandler/FatalHandlerComponentLinuxImpl.cpp b/Svc/FatalHandler/FatalHandlerComponentLinuxImpl.cpp
index ff72572eba..f95329de61 100644
--- a/Svc/FatalHandler/FatalHandlerComponentLinuxImpl.cpp
+++ b/Svc/FatalHandler/FatalHandlerComponentLinuxImpl.cpp
@@ -28,7 +28,7 @@ namespace Svc {
             FwEventIdType Id) {
         // for **nix, delay then exit with error code
         Fw::Logger::logMsg("FATAL %d handled.\n",Id,0,0,0,0,0);
-        (void)Os::Task::delay(1000);
+        (void)Os::Task::delay(Fw::Time(1, 0));
         Fw::Logger::logMsg("Exiting with abort signal and core dump file.\n",0,0,0,0,0,0);
         (void)raise( SIGABRT );
         exit(1);
diff --git a/Svc/LinuxTimer/LinuxTimerComponentImplTaskDelay.cpp b/Svc/LinuxTimer/LinuxTimerComponentImplTaskDelay.cpp
index 4ae15d9365..ef2d02eb92 100644
--- a/Svc/LinuxTimer/LinuxTimerComponentImplTaskDelay.cpp
+++ b/Svc/LinuxTimer/LinuxTimerComponentImplTaskDelay.cpp
@@ -19,7 +19,7 @@ namespace Svc {
 
   void LinuxTimerComponentImpl::startTimer(NATIVE_INT_TYPE interval) {
       while (true) {
-          Os::Task::delay(static_cast<Os::Task::ParamType>(interval));
+          Os::Task::delay(Fw::Time(static_cast<U32>(interval/1000), static_cast<U32>((interval % 1000) * 1000)));
           this->m_mutex.lock();
           bool quit = this->m_quit;
           this->m_mutex.unLock();
diff --git a/cmake/platform/Darwin.cmake b/cmake/platform/Darwin.cmake
index 2cf1e88655..2958c12f72 100644
--- a/cmake/platform/Darwin.cmake
+++ b/cmake/platform/Darwin.cmake
@@ -19,6 +19,7 @@ if (NOT DEFINED FPRIME_USE_BAREMETAL_SCHEDULER)
    FIND_PACKAGE ( Threads REQUIRED )
 endif()
 choose_fprime_implementation(Os/File Os/File/Posix)
+choose_fprime_implementation(Os/Task Os/Task/Posix)
 
 # Add linux include path which is compatible with Darwin for PlatformTypes.hpp
 include_directories(SYSTEM "${CMAKE_CURRENT_LIST_DIR}/types")
diff --git a/cmake/platform/Linux.cmake b/cmake/platform/Linux.cmake
index 286c842012..00c09facfe 100644
--- a/cmake/platform/Linux.cmake
+++ b/cmake/platform/Linux.cmake
@@ -10,6 +10,7 @@ if (NOT DEFINED FPRIME_USE_BAREMETAL_SCHEDULER)
    FIND_PACKAGE ( Threads REQUIRED )
 endif()
 choose_fprime_implementation(Os/File Os/File/Posix)
+choose_fprime_implementation(Os/Task Os/Task/Posix)
 
 # Use common linux setup
 add_definitions(-DTGT_OS_TYPE_LINUX)
diff --git a/config/FpConfig.h b/config/FpConfig.h
index 35e8b01248..cf04c7002f 100644
--- a/config/FpConfig.h
+++ b/config/FpConfig.h
@@ -392,7 +392,7 @@ typedef FwIndexType FwQueueSizeType;
 #endif
 
 #ifndef FW_HANDLE_MAX_SIZE
-#define FW_HANDLE_MAX_SIZE 16  //!< Maximum size of a handle for OS resources (files, queues, locks, etc.)
+#define FW_HANDLE_MAX_SIZE 24  //!< Maximum size of a handle for OS resources (files, queues, locks, etc.)
 #endif
 
 #ifndef FW_HANDLE_ALIGNMENT
diff --git a/config/IpCfg.hpp b/config/IpCfg.hpp
index e699c66ea9..60daae5adb 100644
--- a/config/IpCfg.hpp
+++ b/config/IpCfg.hpp
@@ -9,7 +9,7 @@
 // acknowledged.
 //
 // ======================================================================
-
+#include <Fw/Time/Time.hpp>
 #ifndef REF_IPCFG_HPP
 #define REF_IPCFG_HPP
 
@@ -19,10 +19,9 @@ enum IpCfg {
     SOCKET_IP_SEND_FLAGS = 0,              // send, sendto FLAGS argument
     SOCKET_IP_RECV_FLAGS = 0,              // recv FLAGS argument
     SOCKET_MAX_ITERATIONS = 0xFFFF,        // Maximum send/recv attempts before an error is returned
-    SOCKET_RETRY_INTERVAL_MS = 1000,       // Interval between connection retries before main recv thread starts
     SOCKET_MAX_HOSTNAME_SIZE = 256         // Maximum stored hostname
 };
-
+static const Fw::Time SOCKET_RETRY_INTERVAL = Fw::Time(1, 0);
 
 
 #endif //REF_IPCFG_HPP


Test patch:
diff --git a/Drv/Ip/test/ut/SocketTestHelper.cpp b/Drv/Ip/test/ut/SocketTestHelper.cpp
index aabb532ec9..a52d2e3b7e 100644
--- a/Drv/Ip/test/ut/SocketTestHelper.cpp
+++ b/Drv/Ip/test/ut/SocketTestHelper.cpp
@@ -10,6 +10,7 @@
 #include <unistd.h>
 #include <cerrno>
 #include <arpa/inet.h>
+#include <IpCfg.hpp>
 
 namespace Drv {
 namespace Test {
@@ -65,7 +66,7 @@ bool wait_on_change(Drv::IpSocket &socket, bool open, U32 iterations) {
         if (open == socket.isOpened()) {
             return true;
         }
-        Os::Task::delay(10);
+        Os::Task::delay(Fw::Time(0, 10000));
     }
     return false;
 }
@@ -75,10 +76,15 @@ bool wait_on_started(Drv::IpSocket &socket, bool open, U32 iterations) {
         if (open == socket.isStarted()) {
             return true;
         }
-        Os::Task::delay(10);
+        Os::Task::delay(Fw::Time(0, 10000));
     }
     return false;
 }
 
+U64 get_configured_delay_ms() {
+    return (static_cast<U64>(SOCKET_RETRY_INTERVAL.getSeconds()) * 1000) +
+           (static_cast<U64>(SOCKET_RETRY_INTERVAL.getUSeconds()) / 1000);
+}
+
 };
 };
diff --git a/Drv/Ip/test/ut/SocketTestHelper.hpp b/Drv/Ip/test/ut/SocketTestHelper.hpp
index 39583abda2..ad6e3a2de7 100644
--- a/Drv/Ip/test/ut/SocketTestHelper.hpp
+++ b/Drv/Ip/test/ut/SocketTestHelper.hpp
@@ -65,6 +65,12 @@ bool wait_on_change(Drv::IpSocket &socket, bool open, U32 iterations);
 */
 bool wait_on_started(Drv::IpSocket &socket, bool open, U32 iterations);
 
+/**
+ * Get the configured delay, converted to milliseconds
+ * @return SOCKET_RETRY_INTERVAL converted to milliseconds
+ */
+U64 get_configured_delay_ms();
+
 };
 };
 #endif
diff --git a/Drv/TcpClient/test/ut/TcpClientTester.cpp b/Drv/TcpClient/test/ut/TcpClientTester.cpp
index 4312f154e9..b50f0f180d 100644
--- a/Drv/TcpClient/test/ut/TcpClientTester.cpp
+++ b/Drv/TcpClient/test/ut/TcpClientTester.cpp
@@ -44,7 +44,7 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
     // Start up a receive thread
     if (recv_thread) {
         Os::TaskString name("receiver thread");
-        this->component.startSocketTask(name, true, Os::Task::TASK_DEFAULT, Os::Task::TASK_DEFAULT);
+        this->component.start(name, true, Os::Task::TASK_DEFAULT, Os::Task::TASK_DEFAULT);
     }
 
     // Loop through a bunch of client disconnects
@@ -55,7 +55,7 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
         if (not recv_thread) {
             status1 = this->component.open();
         } else {
-            EXPECT_TRUE(Drv::Test::wait_on_change(this->component.getSocketHandler(), true, SOCKET_RETRY_INTERVAL_MS/10 + 1));
+            EXPECT_TRUE(Drv::Test::wait_on_change(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1));
         }
         EXPECT_TRUE(this->component.getSocketHandler().isOpened());
         status2 = server.open();
@@ -87,8 +87,8 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
         }
         // Properly stop the client on the last iteration
         if ((1 + i) == iterations && recv_thread) {
-            this->component.stopSocketTask();
-            this->component.joinSocketTask(nullptr);
+            this->component.stop();
+            this->component.join();
         } else {
             this->component.close();
         }
diff --git a/Drv/TcpServer/test/ut/TcpServerTester.cpp b/Drv/TcpServer/test/ut/TcpServerTester.cpp
index 48560ebe24..4572cf6705 100644
--- a/Drv/TcpServer/test/ut/TcpServerTester.cpp
+++ b/Drv/TcpServer/test/ut/TcpServerTester.cpp
@@ -37,8 +37,8 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) {
     // Start up a receive thread
     if (recv_thread) {
         Os::TaskString name("receiver thread");
-        this->component.startSocketTask(name, true, Os::Task::TASK_DEFAULT, Os::Task::TASK_DEFAULT);
-        EXPECT_TRUE(Drv::Test::wait_on_started(this->component.getSocketHandler(), true, SOCKET_RETRY_INTERVAL_MS/10 + 1));
+        this->component.start(name, true, Os::Task::TASK_DEFAULT, Os::Task::TASK_DEFAULT);
+        EXPECT_TRUE(Drv::Test::wait_on_started(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1));
     } else {
         serverStat = this->component.startup();
         ASSERT_EQ(serverStat, SOCK_SUCCESS)
@@ -59,7 +59,7 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) {
         if (not recv_thread) {
             status1 = this->component.open();
         } else {
-            EXPECT_TRUE(Drv::Test::wait_on_change(this->component.getSocketHandler(), true, SOCKET_RETRY_INTERVAL_MS/10 + 1));
+            EXPECT_TRUE(Drv::Test::wait_on_change(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1));
         }
         EXPECT_TRUE(this->component.getSocketHandler().isOpened());
 
@@ -93,8 +93,8 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) {
         // Properly stop the client on the last iteration
         if ((1 + i) == iterations && recv_thread) {
             this->component.shutdown();
-            this->component.stopSocketTask();
-            this->component.joinSocketTask(nullptr);
+            this->component.stop();
+            this->component.join();
         } else {
             this->component.close();
         }
diff --git a/Drv/Udp/test/ut/UdpTester.cpp b/Drv/Udp/test/ut/UdpTester.cpp
index fbae75a30e..d42de17d98 100644
--- a/Drv/Udp/test/ut/UdpTester.cpp
+++ b/Drv/Udp/test/ut/UdpTester.cpp
@@ -51,7 +51,7 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) {
     // Start up a receive thread
     if (recv_thread) {
         Os::TaskString name("receiver thread");
-        this->component.startSocketTask(name, true, Os::Task::TASK_DEFAULT, Os::Task::TASK_DEFAULT);
+        this->component.start(name, true, Os::Task::TASK_DEFAULT, Os::Task::TASK_DEFAULT);
     }
 
     // Loop through a bunch of client disconnects
@@ -69,7 +69,7 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) {
                 << "Port2: " << port2;
 
         } else {
-            EXPECT_TRUE(Drv::Test::wait_on_change(this->component.getSocketHandler(), true, SOCKET_RETRY_INTERVAL_MS/10 + 1));
+            EXPECT_TRUE(Drv::Test::wait_on_change(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1));
         }
         EXPECT_TRUE(this->component.getSocketHandler().isOpened());
 
@@ -106,8 +106,8 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) {
         }
         // Properly stop the client on the last iteration
         if ((1 + i) == iterations && recv_thread) {
-            this->component.stopSocketTask();
-            this->component.joinSocketTask(nullptr);
+            this->component.stop();
+            this->component.join();
         } else {
             this->component.close();
         }
diff --git a/Os/Posix/test/ut/PosixTaskTests.cpp b/Os/Posix/test/ut/PosixTaskTests.cpp
new file mode 100644
index 0000000000..ee03b3f2ea
--- /dev/null
+++ b/Os/Posix/test/ut/PosixTaskTests.cpp
@@ -0,0 +1,8 @@
+#include <gtest/gtest.h>
+#include "STest/Scenario/Scenario.hpp"
+
+int main(int argc, char** argv) {
+    STest::Random::seed();
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/Os/Stub/test/DefaultFile.cpp b/Os/Stub/test/DefaultFile.cpp
index e40651c4e5..d35514e7e8 100644
--- a/Os/Stub/test/DefaultFile.cpp
+++ b/Os/Stub/test/DefaultFile.cpp
@@ -3,30 +3,17 @@
 // \brief sets default Os::File to test stub implementation via linker
 // ======================================================================
 #include "Os/Stub/test/File.hpp"
-#include "Fw/Types/Assert.hpp"
-#include <new>
+#include "Os/Delegate.hpp"
+
 namespace Os {
-//! \brief get implementation file interface for use as delegate
-//!
-//! Added via linker to ensure that the tracking test stub implementation of Os::File is the default.
-//!
-//! \param aligned_placement_new_memory: memory to placement-new into
-//! \return FileInterface pointer result of placement new
-//!
-FileInterface *FileInterface::getDelegate(U8 *aligned_placement_new_memory, const FileInterface* to_copy) {
-    FW_ASSERT(aligned_placement_new_memory != nullptr);
-    const Os::Stub::File::Test::TestFile* copy_me = reinterpret_cast<const Os::Stub::File::Test::TestFile*>(to_copy);
-    // Placement-new the file handle into the opaque file-handle storage
-    static_assert(sizeof(Os::Stub::File::Test::TestFile) <= sizeof Os::File::m_handle_storage,
-            "Handle size not large enough");
-    static_assert((FW_HANDLE_ALIGNMENT % alignof(Os::Stub::File::Test::TestFile)) == 0, "Handle alignment invalid");
-    Os::Stub::File::Test::TestFile *interface = nullptr;
-    if (to_copy == nullptr) {
-        interface = new(aligned_placement_new_memory) Os::Stub::File::Test::TestFile;
-    } else {
-        interface = new(aligned_placement_new_memory) Os::Stub::File::Test::TestFile(*copy_me);
-    }
-    FW_ASSERT(interface != nullptr);
-    return interface;
+
+//! \brief get a delegate for FileInterface that intercepts calls for  for stub test file usage
+//! \param aligned_new_memory: aligned memory to fill
+//! \param to_copy: pointer to copy-constructor input
+//! \return: pointer to delegate
+FileInterface *FileInterface::getDelegate(HandleStorage& aligned_placement_new_memory, const FileInterface* to_copy) {
+    return Os::Delegate::makeDelegate<FileInterface, Os::Stub::File::Test::TestFile>(
+            aligned_placement_new_memory, to_copy
+    );
 }
 }
diff --git a/Os/Stub/test/DefaultTask.cpp b/Os/Stub/test/DefaultTask.cpp
new file mode 100644
index 0000000000..32a5b3036c
--- /dev/null
+++ b/Os/Stub/test/DefaultTask.cpp
@@ -0,0 +1,39 @@
+// ======================================================================
+// \title Os/Stub/test/DefaultTask.cpp
+// \brief sets default Os::Task to test stub implementation via linker
+// ======================================================================
+#include <cerrno>
+#include "Os/Task.hpp"
+#include "Os/Stub/test/Task.hpp"
+#include "Os/Delegate.hpp"
+#include <sys/time.h>
+
+namespace Os {
+    Os::Task::Status TaskInterface::delay(Fw::Time interval) {
+        Os::Stub::Task::Test::StaticData::data.lastCalled = Os::Stub::Task::Test::StaticData::LastFn::DELAY_FN;
+        Os::Stub::Task::Test::StaticData::data.delay = interval;
+
+        // For testing stub, the default implementation of delay for a "task" is a busy wait. This acts as a synthetic
+        // albeit inefficient implementation.
+        timeval start;
+        timeval end;
+        if (gettimeofday(&start, nullptr) == 0) {
+            end.tv_usec = (start.tv_usec + interval.getUSeconds()) % 1000000;
+            end.tv_sec = start.tv_sec + interval.getSeconds() + (start.tv_usec + interval.getUSeconds())/1000000;
+            // Bounded busy wait
+            for (U64 wait = 0; wait < std::numeric_limits<U64>::max(); wait++) {
+                gettimeofday(&start, nullptr);
+                if (((start.tv_sec >= end.tv_sec) && (start.tv_usec >= end.tv_usec)) ||
+                    (start.tv_sec > end.tv_sec)) {
+                    break;
+                }
+            }
+        }
+        return Os::Stub::Task::Test::StaticData::data.delayStatus;
+    }
+
+    TaskInterface* TaskInterface::getDelegate(HandleStorage& aligned_new_memory) {
+        return Os::Delegate::makeDelegate<TaskInterface, Os::Stub::Task::Test::TestTask>(aligned_new_memory);
+    }
+
+}
diff --git a/Os/Stub/test/Task.cpp b/Os/Stub/test/Task.cpp
new file mode 100644
index 0000000000..40e3cd5ba2
--- /dev/null
+++ b/Os/Stub/test/Task.cpp
@@ -0,0 +1,55 @@
+//
+// Created by Michael Starch on 4/12/24.
+//
+
+#include "Os/Stub/test/Task.hpp"
+namespace Os {
+namespace Stub {
+namespace Task {
+namespace Test {
+
+StaticData StaticData::data;
+
+TestTask::TestTask() {
+    StaticData::data.lastCalled = StaticData::LastFn::CONSTRUCT_FN;
+}
+
+TestTask::~TestTask() {
+    StaticData::data.lastCalled = StaticData::LastFn::DESTRUCT_FN;
+}
+
+void TestTask::onStart() {
+
+}
+
+Os::TaskInterface::Status TestTask::join() {
+    StaticData::data.lastCalled = StaticData::LastFn::JOIN_FN;
+    return StaticData::data.joinStatus;
+}
+
+void TestTask::suspend(Os::TaskInterface::SuspensionType suspensionType) {
+    StaticData::data.suspensionType = suspensionType;
+    StaticData::data.lastCalled = StaticData::LastFn::SUSPEND_FN;
+
+}
+
+void TestTask::resume() {
+    StaticData::data.lastCalled = StaticData::LastFn::RESUME_FN;
+}
+
+Os::TaskHandle *TestTask::getHandle() {
+    return nullptr;
+}
+
+Os::TaskInterface::Status TestTask::start(const Os::TaskInterface::Arguments &arguments) {
+    StaticData::data.lastCalled = StaticData::LastFn::START_FN;
+    return StaticData::data.startStatus;
+}
+
+bool TestTask::isCooperative() {
+    return true;
+}
+}
+}
+}
+}
diff --git a/Os/Stub/test/Task.hpp b/Os/Stub/test/Task.hpp
new file mode 100644
index 0000000000..2a972e65ab
--- /dev/null
+++ b/Os/Stub/test/Task.hpp
@@ -0,0 +1,109 @@
+#include "Os/Task.hpp"
+
+#ifndef OS_STUB_TASK_TEST_HPP
+#define OS_STUB_TASK_TEST_HPP
+namespace Os {
+namespace Stub {
+namespace Task {
+namespace Test {
+
+//! Data that supports the stubbed File implementation.
+//!/
+struct StaticData {
+    //! Enumeration of last function called
+    //!
+    enum LastFn {
+        NONE_FN,
+        CONSTRUCT_FN,
+        DESTRUCT_FN,
+        DELAY_FN,
+        JOIN_FN,
+        SUSPEND_FN,
+        RESUME_FN,
+        START_FN
+    };
+    StaticData() = default;
+    ~StaticData() = default;
+
+    //! Last function called
+    LastFn lastCalled = NONE_FN;
+    //Os::Task::Arguments arguments();
+    Os::Task::SuspensionType suspensionType;
+    Fw::Time delay;
+
+    Os::Task::Status delayStatus = Os::Task::Status::OP_OK;
+    Os::Task::Status joinStatus = Os::Task::Status::OP_OK;
+    Os::Task::Status startStatus = Os::Task::Status::OP_OK;
+
+    // Singleton data
+    static StaticData data;
+};
+
+//! Test task handle
+class TestTaskHandle : public TaskHandle {};
+
+//! Implementation of task
+class TestTask : public TaskInterface {
+  public:
+    //! Constructor
+    TestTask();
+
+    //! Destructor
+    ~TestTask() override;
+
+    //! \brief perform required task start actions
+    void onStart() override;
+
+    //! \brief block until the task has ended
+    //!
+    //! Blocks the current (calling) task until this task execution has ended. Callers should ensure that any
+    //! signals required to stop this task have already been emitted or will be emitted by another task.
+    //!
+    //! \return status of the block
+    Status join() override;
+
+    //! \brief suspend the task given the suspension type
+    //!
+    //! Suspends the task. Some implementations track if the suspension of a task was intentional or
+    //! unintentional. The supplied `suspensionType` parameter indicates that this was intentional or
+    //! unintentional. The type of suspension is also returned when calling `isSuspended`.
+    //!
+    //! \param suspensionType intentionality of the suspension
+    void suspend(SuspensionType suspensionType) override;
+
+    //! \brief resume a suspended task
+    //!
+    //! Resumes this task. Not started, running, and exited tasks take no action.
+    //!
+    void resume() override;
+
+    //! \brief return the underlying task handle (implementation specific)
+    //! \return internal task handle representation
+    TaskHandle* getHandle() override;
+
+    //! \brief determine if the task requires cooperative multitasking
+    //!
+    //! Some task implementations require cooperative multitasking where the task execution is run by a user
+    //! defined task scheduler and not the operating system task scheduler. These tasks cooperatively on
+    //! multitask by doing one unit of work and return from the function.
+    //!
+    //! This function indicates if the task requires cooperative support.
+    //! The default implementation returns false.
+    //!
+    //! \return true when the task expects cooperation, false otherwise
+    bool isCooperative() override;
+
+    //! \brief start the task
+    //!
+    //! Starts the task given the supplied arguments.
+    //!
+    //! \param arguments: arguments supplied to the task start call
+    //! \return status of the task start
+    Status start(const Arguments& arguments) override;
+};
+
+}
+}
+}
+}
+#endif // End OS_STUB_TASK_TEST_HPP
diff --git a/Os/Stub/test/ut/StubFileTests.cpp b/Os/Stub/test/ut/StubFileTests.cpp
index 8ec6f35744..caea882ee3 100644
--- a/Os/Stub/test/ut/StubFileTests.cpp
+++ b/Os/Stub/test/ut/StubFileTests.cpp
@@ -211,5 +211,6 @@ TEST(FppTypes, FileModeEnum) {
 
 int main(int argc, char **argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    STest::Random::seed();
     return RUN_ALL_TESTS();
 }
diff --git a/Os/Stub/test/ut/StubTaskTests.cpp b/Os/Stub/test/ut/StubTaskTests.cpp
new file mode 100644
index 0000000000..39ef52bd20
--- /dev/null
+++ b/Os/Stub/test/ut/StubTaskTests.cpp
@@ -0,0 +1,144 @@
+// ======================================================================
+// \title Os/Stub/test/ut/StubTaskTests.cpp
+// \brief tests using stub implementation for Os::Task interface testing
+// ======================================================================
+#include <gtest/gtest.h>
+#include "Os/Models/Models.hpp"
+#include "Os/test/ut/task/CommonTests.hpp"
+#include "Os/test/ut/task/RulesHeaders.hpp"
+#include "Os/Stub/test/Task.hpp"
+#include "Os/Models/Models.hpp"
+
+using namespace Os::Stub::Task::Test;
+
+void testMethod(void* test) {
+
+}
+
+// Basic file tests
+class Interface : public ::testing::Test {
+public:
+    //! Setup function delegating to UT setUp function
+    void SetUp() override {
+        StaticData::data = StaticData();
+    }
+
+    //! Setup function delegating to UT tearDown function
+    void TearDown() override {
+        StaticData::data = StaticData();
+    }
+};
+
+struct QuickRegistry : Os::TaskRegistry {
+
+    void addTask(Os::Task* task) override {
+        this->m_task = task;
+    }
+
+    void removeTask(Os::Task* task) override {
+        this->m_task = nullptr;
+    }
+    Os::Task* m_task = nullptr;
+};
+
+
+// Ensure that Os::Task properly calls the implementation constructor
+TEST_F(Interface, Construction) {
+    Os::Task task;
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::CONSTRUCT_FN);
+}
+
+// Ensure that Os::Task properly calls the implementation destructor
+TEST_F(Interface, Destruction) {
+    delete (new Os::Task);
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::DESTRUCT_FN);
+}
+
+// Ensure that Os::Task properly calls the implementation start
+TEST_F(Interface, Start) {
+    Os::Task task;
+    StaticData::data.startStatus = Os::Task::Status::UNKNOWN_ERROR;
+    Os::Task::Arguments arguments(Fw::String("Task"), &testMethod, nullptr);
+    ASSERT_EQ(task.start(arguments), StaticData::data.startStatus);
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::START_FN);
+}
+
+// Ensure that Os::Task properly calls the implementation start with arguments
+TEST_F(Interface, StartOptionalArguments) {
+    Os::Task task;
+    StaticData::data.startStatus = Os::Task::Status::INVALID_STACK;
+    Os::Task::Arguments arguments(Fw::String("Task"), &testMethod, nullptr);
+    ASSERT_EQ(task.start(arguments), StaticData::data.startStatus);
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::START_FN);
+}
+
+// Ensure that Os::Task properly calls the implementation onStart before calling start
+TEST_F(Interface, OnStart) {
+    Os::Task task;
+    StaticData::data.startStatus = Os::Task::Status::UNKNOWN_ERROR;
+    Os::Task::Arguments arguments(Fw::String("Task"), &testMethod, nullptr);
+    ASSERT_EQ(task.start(arguments), StaticData::data.startStatus);
+
+}
+
+// Ensure that Os::Task properly calls the implementation suspend
+TEST_F(Interface, Suspend) {
+    Os::Task task;
+    task.suspend();
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::SUSPEND_FN);
+}
+
+// Ensure that Os::Task properly calls the implementation resume
+TEST_F(Interface, Resume) {
+    Os::Task task;
+    StaticData::data.startStatus = Os::Task::Status::UNKNOWN_ERROR;
+    task.resume();
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::RESUME_FN);
+}
+
+// Ensure that Os::Task properly calls the implementation join
+TEST_F(Interface, Join) {
+    Os::Task task;
+    StaticData::data.joinStatus = Os::Task::Status::JOIN_ERROR;
+    StaticData::data.startStatus = Os::Task::Status::JOIN_ERROR;
+    Os::Task::Arguments arguments(Fw::String("Task"), &testMethod, nullptr);
+    ASSERT_EQ(task.start(arguments), StaticData::data.startStatus);
+    task.invokeRoutine();
+    ASSERT_EQ(task.join(), StaticData::data.joinStatus);
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::JOIN_FN);
+}
+
+// Ensure that Os::Task properly calls the implementation getHandle
+TEST_F(Interface, GetHandle) {
+    Os::Task task;
+}
+
+// Ensure that Os::Task properly calls the implementation delay
+TEST_F(Interface, Delay) {
+    Os::Task task;
+    StaticData::data.delayStatus = Os::Task::Status::DELAY_ERROR;
+    ASSERT_EQ(Os::Task::delay(Fw::Time(0, 1)), StaticData::data.delayStatus);
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::DELAY_FN);
+}
+
+// Ensure that Os::Task properly calls the registry removal function
+TEST_F(Interface, RegistryRemove) {
+    QuickRegistry registry;
+    Os::Task::registerTaskRegistry(&registry);
+    Os::Task* task = new Os::Task;
+    StaticData::data.startStatus = Os::Task::Status::OP_OK;
+    Os::Task::Arguments arguments(Fw::String("Task"), &testMethod, nullptr);
+    ASSERT_EQ(task->start(arguments), StaticData::data.startStatus);
+    Os::Task::s_numTasks = 0; // Reset task count for rules-based testing
+    ASSERT_EQ(registry.m_task, task);
+    delete task;
+    ASSERT_EQ(registry.m_task, nullptr);
+
+
+}
+
+int main(int argc, char **argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    STest::Random::seed();
+    return RUN_ALL_TESTS();
+}
diff --git a/Os/test/ut/IntervalTimerTest.cpp b/Os/test/ut/IntervalTimerTest.cpp
index 9b9d70fe61..94359d6506 100644
--- a/Os/test/ut/IntervalTimerTest.cpp
+++ b/Os/test/ut/IntervalTimerTest.cpp
@@ -10,7 +10,7 @@ extern "C" {
 void intervalTimerTest() {
     Os::IntervalTimer timer;
     timer.start();
-    Os::Task::delay(1000);
+    Os::Task::delay(Fw::Time(1, 0));
     timer.stop();
     ASSERT_GE(timer.getDiffUsec(), 1000000);
     ASSERT_LT(timer.getDiffUsec(), 1005000);
diff --git a/Os/test/ut/OsTaskTest.cpp b/Os/test/ut/OsTaskTest.cpp
deleted file mode 100644
index bd6f623410..0000000000
--- a/Os/test/ut/OsTaskTest.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "gtest/gtest.h"
-#include <Os/Task.hpp>
-#include <cstdio>
-
-extern "C" {
-    void startTestTask();
-}
-
-void someTask(void* ptr) {
-    bool* ran = static_cast<bool*>(ptr);
-    *ran = true;
-}
-
-void startTestTask() {
-    volatile bool taskRan = false;
-    Os::Task testTask;
-    Os::TaskString name("ATestTask");
-    Os::Task::TaskStatus stat = testTask.start(name,someTask,const_cast<bool*>(&taskRan));
-    ASSERT_EQ(stat, Os::Task::TASK_OK);
-    testTask.join(nullptr);
-    ASSERT_EQ(taskRan, true);
-}
diff --git a/Os/test/ut/OsTestMain.cpp b/Os/test/ut/OsTestMain.cpp
index aed55dae9d..fb29cdac4d 100644
--- a/Os/test/ut/OsTestMain.cpp
+++ b/Os/test/ut/OsTestMain.cpp
@@ -4,7 +4,6 @@
 #include <cstdio>
 
 extern "C" {
-  void startTestTask();
   void qtest_block_receive();
   void qtest_nonblock_receive();
   void qtest_nonblock_send();
@@ -18,9 +17,6 @@ extern "C" {
   void mutexBasicLockableTest();
 }
 const char* filename;
-TEST(Nominal, StartTestTask) {
-   startTestTask();
-}
 TEST(Nominal, QTestBlockRecv) {
    qtest_block_receive();
 }
diff --git a/Os/test/ut/file/CommonTests.cpp b/Os/test/ut/file/CommonTests.cpp
index 845fdbeb26..cd74a81593 100644
--- a/Os/test/ut/file/CommonTests.cpp
+++ b/Os/test/ut/file/CommonTests.cpp
@@ -1,5 +1,5 @@
 // ======================================================================
-// \title Os/test/ut/file/CommonFileTests.cpp
+// \title Os/test/ut/file/CommonTests.cpp
 // \brief common test implementations
 // ======================================================================
 #include "Os/test/ut/file/CommonTests.hpp"
@@ -268,9 +268,7 @@ TEST_F(FunctionalIO, Preallocate) {
 // Randomized testing on the interfaces
 TEST_F(Functionality, RandomizedInterfaceTesting) {
     // Enumerate all rules and construct an instance of each
-    Os::Test::File::Tester::OpenFileCreate open_file_create_rule(true);
     Os::Test::File::Tester::OpenFileCreateOverwrite open_file_create_overwrite_rule(true);
-    Os::Test::File::Tester::OpenForWrite open_for_write_rule(true);
     Os::Test::File::Tester::CloseFile close_file_rule;
     Os::Test::File::Tester::CopyConstruction copy_construction;
     Os::Test::File::Tester::CopyAssignment copy_assignment;
@@ -294,9 +292,7 @@ TEST_F(Functionality, RandomizedInterfaceTesting) {
 
     // Place these rules into a list of rules
     STest::Rule<Os::Test::File::Tester>* rules[] = {
-            &open_file_create_rule,
             &open_file_create_overwrite_rule,
-            &open_for_write_rule,
             &close_file_rule,
             &copy_assignment,
             &copy_construction,
diff --git a/Os/test/ut/file/CommonTests.hpp b/Os/test/ut/file/CommonTests.hpp
index ca61efc7be..dda9fd233e 100644
--- a/Os/test/ut/file/CommonTests.hpp
+++ b/Os/test/ut/file/CommonTests.hpp
@@ -1,5 +1,5 @@
 // ======================================================================
-// \title Os/test/ut/file/CommonFileTests.hpp
+// \title Os/test/ut/file/CommonTests.hpp
 // \brief definitions used in common file testing
 // ======================================================================
 #include <Os/File.hpp>
diff --git a/Os/test/ut/file/FileRules.cpp b/Os/test/ut/file/FileRules.cpp
index c07260e35e..868c4053f0 100644
--- a/Os/test/ut/file/FileRules.cpp
+++ b/Os/test/ut/file/FileRules.cpp
@@ -265,7 +265,8 @@ void Os::Test::File::Tester::OpenBaseRule::action(Os::Test::File::Tester &state
 
     // Perform action and shadow action asserting the results are the same
     Os::File::Status status = state.m_file.open(filename->c_str(), m_mode, this->m_overwrite);
-    ASSERT_EQ(status, state.shadow_open(*filename, m_mode, this->m_overwrite));
+    Os::File::Status s2 = state.shadow_open(*filename, m_mode, this->m_overwrite);
+    ASSERT_EQ(status, s2);
 
     // Extra check to ensure file is consistently open
     if (Os::File::Status::OP_OK == status) {
diff --git a/Os/test/ut/task/CommonTests.cpp b/Os/test/ut/task/CommonTests.cpp
new file mode 100644
index 0000000000..23927a9a44
--- /dev/null
+++ b/Os/test/ut/task/CommonTests.cpp
@@ -0,0 +1,257 @@
+// ======================================================================
+// \title Os/test/ut/task/CommonTests.cpp
+// \brief common test implementations
+// ======================================================================
+#include <gtest/gtest.h>
+#include "Os/Task.hpp"
+#include "Os/test/ut/task/CommonTests.hpp"
+#include "Os/test/ut/task/RulesHeaders.hpp"
+#include "Fw/Types/String.hpp"
+
+static constexpr U32 RANDOM_BOUND = 1000;
+
+namespace Os {
+namespace Test {
+namespace Task {
+    FwSizeType TestTaskInfo::s_task_count = 0;
+
+    Tester::~Tester() {
+        this->m_tasks.erase(this->m_tasks.begin(), this->m_tasks.end());
+    }
+
+    TestTaskInfo::~TestTaskInfo() {
+        this->stop();
+    }
+
+    void TestTaskInfo::step() {
+        this->m_lock.lock();
+        // Read update and clear signal
+        if (this->m_signal) {
+            this->m_stage = (this->m_stage == Lifecycle::END) ? Lifecycle::END : static_cast<Lifecycle>(this->m_stage + 1);
+        }
+        this->m_signal = false;
+        this->m_lock.unlock();
+    }
+
+    void TestTaskInfo::signal() {
+        // Block waiting for m_signal to be false
+        this->m_lock.lock();
+        this->m_signal = true;
+        this->m_lock.unlock();
+    }
+
+    TestTaskInfo::Lifecycle TestTaskInfo::stage() {
+        Lifecycle stage = Lifecycle::UNSET;
+        this->m_lock.lock();
+        stage = this->m_stage;
+        this->m_lock.unlock();
+        return stage;
+    }
+
+    void TestTaskInfo::start(Os::Task::taskRoutine routine) {
+        Fw::String name("TaskName");
+        Os::Task::Arguments arguments(name, routine, this);
+        s_task_count += 1;
+        this->m_task.start(arguments);
+    }
+
+    void TestTaskInfo::stop() {
+        this->m_lock.lock();
+        Os::Task::State state = this->m_state;
+        this->m_lock.unlock();
+        // Not started task, break out
+        if (state == Os::Task::State::NOT_STARTED) {
+            return;
+        }
+        while (this->stage() != Lifecycle::END) {
+            this->signal();
+            // Run cooperative multitasking when necessary
+            Tester::getCurrentRegistry()->invokeRoutines();
+        }
+        this->m_task.join();
+    }
+
+    void TestTaskInfo::standard_task(void *argument) {
+        ASSERT_NE(argument, nullptr) << "Test provided bad argument pointer";
+        TestTaskInfo& task_info = *reinterpret_cast<TestTaskInfo*>(argument);
+        // On the first iteration set start-up items
+        if (task_info.stage() == TestTaskInfo::Lifecycle::UNSET) {
+            task_info.m_lock.lock();
+            task_info.m_stage = Lifecycle::BEGINNING;
+            task_info.m_state = Os::Task::RUNNING;
+            task_info.m_lock.unlock();
+        }
+        // Loop when the thread is not cooperative and lifecycle is not over
+        do {
+            task_info.step();
+        } while ((not task_info.m_task.isCooperative()) && (task_info.stage() != TestTaskInfo::Lifecycle::END));
+        // Update if in end stage
+        if (task_info.stage() == TestTaskInfo::Lifecycle::END) {
+            task_info.m_state = Os::Task::State::EXITED;
+        }
+    }
+
+    void TestTaskInfo::joining_task(void *argument) {
+        ASSERT_NE(argument, nullptr) << "Test provided bad argument pointer";
+        TestTaskInfo& task_info = *reinterpret_cast<TestTaskInfo*>(argument);
+        ASSERT_NE(task_info.m_other, nullptr) << "Other pointer not properly set";
+        // On the first iteration set start-up items
+        if (task_info.stage() == TestTaskInfo::Lifecycle::UNSET) {
+            task_info.m_lock.lock();
+            task_info.m_stage = Lifecycle::BEGINNING;
+            task_info.m_state = Os::Task::RUNNING;
+            task_info.m_lock.unlock();
+        }
+
+        do {
+            // Signal will come from test thread then this will join
+            if (task_info.stage() == TestTaskInfo::Lifecycle::MIDDLE) {
+                task_info.m_other->m_task.join();
+                task_info.m_lock.lock();
+                task_info.m_stage = TestTaskInfo::Lifecycle::END;
+                task_info.m_lock.unlock();
+                break;
+            }
+            task_info.step();
+        } while (not task_info.m_task.isCooperative());
+    }
+
+    Tester* Tester::s_current_registry = nullptr;
+
+    Tester::Tester() {
+        Tester::s_current_registry = this;
+        Os::Task::registerTaskRegistry(this);
+    }
+
+    Tester* Tester::getCurrentRegistry() {
+        EXPECT_NE(s_current_registry, nullptr) << "Registry not properly initialized";
+        return s_current_registry;
+    }
+
+    void Tester::invokeRoutines() {
+        for (Os::Task* task : this->m_all_tasks) {
+            if (task->isCooperative()) {
+                task->invokeRoutine();
+            }
+        }
+    }
+
+    void Tester::addTask(Os::Task *task) {
+        this->m_last_task = task;
+        this->m_all_tasks.push_back(task);
+        ASSERT_GT(this->m_all_tasks.size(), 0) << "Where have all the tasks gone?";
+    }
+
+    void Tester::removeTask(Os::Task *task) {
+        ASSERT_GT(this->m_all_tasks.size(), 0) << "Where have all the tasks gone?";
+        this->m_last_task = nullptr;
+        size_t i = 0;
+        for (i = 0; i < this->m_all_tasks.size(); i++) {
+            if (task == this->m_all_tasks[i]) {
+                break;
+            }
+        }
+        ASSERT_LT(i, this->m_all_tasks.size()) << "Task not found";
+        this->m_all_tasks.erase(this->m_all_tasks.begin() + i);
+    }
+}
+}
+}
+
+// Ensure that a task can start
+TEST(Functionality, StartTask) {
+    Os::Test::Task::Tester tester;
+    Os::Test::Task::Tester::Start rule;
+    rule.apply(tester);
+}
+
+// Ensure that a started task can be joined to
+TEST(Functionality, StartJoinTask) {
+    Os::Test::Task::Tester tester;
+    Os::Test::Task::Tester::Start start_rule;
+    Os::Test::Task::Tester::Join join_rule;
+    start_rule.apply(tester);
+    join_rule.apply(tester);
+
+}
+
+// Ensure that state transitions work reliably
+TEST(Functionality, CheckState) {
+    Os::Test::Task::Tester tester;
+    Os::Test::Task::Tester::CheckState check_rule;
+    Os::Test::Task::Tester::Start start_rule;
+    Os::Test::Task::Tester::Join join_rule;
+    check_rule.apply(tester);
+    start_rule.apply(tester);
+    check_rule.apply(tester);
+    join_rule.apply(tester);
+    check_rule.apply(tester);
+}
+
+// Ensure that delay works as expected
+TEST(Functionality, Delay) {
+    Os::Test::Task::Tester tester;
+    Os::Test::Task::Tester::Delay rule;
+    rule.apply(tester);
+}
+
+// Ensure task counting works
+TEST(Functionality, CheckTaskCount) {
+    Os::Test::Task::Tester tester;
+    Os::Test::Task::Tester::Start start_rule;
+    Os::Test::Task::Tester::CheckTaskCount check_rule;
+    check_rule.apply(tester);
+    start_rule.apply(tester);
+    check_rule.apply(tester);
+}
+
+// Ensure that a join on an unstarted thread returns a bad status
+TEST(Functionality, JoinInvalidState) {
+    Os::Test::Task::Tester tester;
+    Os::Test::Task::Tester::JoinInvalidState rule;
+    rule.apply(tester);
+}
+
+// Ensure that a start with a nullptr causes an assertion failure
+TEST(Functionality, StartIllegalRoutine) {
+    Os::Test::Task::Tester tester;
+    Os::Test::Task::Tester::StartIllegalRoutine rule;
+    rule.apply(tester);
+}
+
+// Randomized testing for the win
+TEST(Functionality, RandomizedTesting) {
+    Os::Test::Task::Tester tester;
+    // Enumerate all rules and construct an instance of each
+    Os::Test::Task::Tester::Start start_rule;
+    Os::Test::Task::Tester::Join join_rule;
+    Os::Test::Task::Tester::CheckState check_state_rule;
+    Os::Test::Task::Tester::CheckTaskCount check_count_rule;
+    Os::Test::Task::Tester::JoinInvalidState invalid_join_rule;
+
+    // Place these rules into a list of rules
+    STest::Rule<Os::Test::Task::Tester>* rules[] = {
+            &start_rule,
+            &join_rule,
+            &check_state_rule,
+            &check_count_rule,
+            &invalid_join_rule
+    };
+
+    // Take the rules and place them into a random scenario
+    STest::RandomScenario<Os::Test::Task::Tester> random(
+            "Random Rules",
+            rules,
+            FW_NUM_ARRAY_ELEMENTS(rules)
+    );
+
+    // Create a bounded scenario wrapping the random scenario
+    STest::BoundedScenario<Os::Test::Task::Tester> bounded(
+            "Bounded Random Rules Scenario",
+            random,
+            RANDOM_BOUND
+    );
+    // Run!
+    const U32 numSteps = bounded.run(tester);
+    printf("Ran %u steps.\n", numSteps);
+}
diff --git a/Os/test/ut/task/CommonTests.hpp b/Os/test/ut/task/CommonTests.hpp
new file mode 100644
index 0000000000..b269fc4bf1
--- /dev/null
+++ b/Os/test/ut/task/CommonTests.hpp
@@ -0,0 +1,7 @@
+//
+// Created by Michael Starch on 4/11/24.
+//
+#include "RulesHeaders.hpp"
+#ifndef OS_TEST_UT_TASK_COMMON_TESTS_HPP
+#define OS_TEST_UT_TASK_COMMON_TESTS_HPP
+#endif //OS_TEST_UT_TASK_COMMON_TESTS_HPP
diff --git a/Os/test/ut/task/RulesHeaders.hpp b/Os/test/ut/task/RulesHeaders.hpp
new file mode 100644
index 0000000000..19d757c58a
--- /dev/null
+++ b/Os/test/ut/task/RulesHeaders.hpp
@@ -0,0 +1,101 @@
+
+
+#ifndef __RULES_HEADERS__
+#define __RULES_HEADERS__
+
+#include "STest/Rule/Rule.hpp"
+#include "STest/Scenario/BoundedScenario.hpp"
+#include "STest/Scenario/RandomScenario.hpp"
+#include "STest/Scenario/Scenario.hpp"
+#include "Os/Task.hpp"
+#include "Os/Mutex.hpp"
+#include <vector>
+
+namespace Os {
+namespace Test {
+namespace Task {
+
+
+
+struct TestTaskInfo {
+    //! Test task life-cycle stages
+    enum Lifecycle {
+        BEGINNING = 0, //!< The initial stage of the task
+        MIDDLE = 1, //!< The task function has taken hold
+        END = 2, //!< The task has been asked to exit
+        UNSET = -1,
+    };
+    static FwSizeType s_task_count;
+
+    Lifecycle m_stage = Lifecycle::UNSET;
+    Os::Mutex m_lock;
+    Os::Task::State m_state = Os::Task::State::NOT_STARTED; //!< Shadow state of the task
+    Os::Task m_task; //!< Task under test
+    std::shared_ptr<TestTaskInfo> m_other = nullptr;
+    bool m_signal = false;
+
+    ~TestTaskInfo();
+
+
+    //! Atomically step through lifecycle stages
+    void step();
+
+    //! Signal a step through lifecycle stage
+    void signal();
+
+    //! Get lifecycle stage
+    Lifecycle stage();
+
+    //! Stop and join thread
+    void stop();
+
+    //! Start the thread
+    void start(Os::Task::taskRoutine routine);
+
+    //! Standard task implementation which waits at each lifecycle stage
+    static void standard_task(void* argument);
+
+    //! Joining task implementation which waits at each lifecycle stage
+    static void joining_task(void* argument);
+};
+
+struct Tester : public Os::TaskRegistry {
+  private:
+    static constexpr U32 MAX_THREAD_COUNT = 100;
+    static constexpr U32 MAX_DELAY_MICRO_SECONDS = 10000;
+
+    std::vector<std::shared_ptr<TestTaskInfo>> m_tasks;
+    Os::Task* m_last_task = nullptr;
+    std::vector<Os::Task*> m_all_tasks;
+
+  public:
+    //! Constructor
+    Tester();
+    virtual ~Tester();
+
+    //! Get the current tester
+    static Tester* getCurrentRegistry();
+
+    //! Invoke cooperative tasks when looping
+    void invokeRoutines();
+
+    //! Add task to test registry
+    //! \param task: task to add
+    void addTask(Os::Task* task) override;
+
+    //! Remove task to test registry
+    //! \param task: task to add
+    void removeTask(Os::Task* task) override;
+
+    static Tester* s_current_registry;
+
+  public:
+#include "TaskRules.hpp"
+};
+
+}
+}
+}
+
+
+#endif
diff --git a/Os/test/ut/task/TaskRules.cpp b/Os/test/ut/task/TaskRules.cpp
new file mode 100644
index 0000000000..bca1289696
--- /dev/null
+++ b/Os/test/ut/task/TaskRules.cpp
@@ -0,0 +1,240 @@
+
+
+#include "RulesHeaders.hpp"
+#include "STest/Pick/Pick.hpp"
+#include "Fw/Types/String.hpp"
+#include <sys/time.h>
+
+
+void wait_for_state_with_timeout(Os::Test::Task::TestTaskInfo &info, const Os::Test::Task::TestTaskInfo::Lifecycle &stage,
+                                 const FwSizeType delay_ms) {
+    // Loop waiting for transition in preparation for join (with timeout)
+    FwSizeType i = 0;
+    for (i = 0; i < (delay_ms * 100); i++) {
+        Os::Task::delay(Fw::Time(0, 10));
+        if (info.stage() == stage) {
+            break;
+        }
+        // Run cooperative multitasking when necessary
+        Os::Test::Task::Tester::getCurrentRegistry()->invokeRoutines();
+    }
+    ASSERT_LT(i, (delay_ms * 100)) << "Task did not find stage " << stage << " within " << delay_ms << "ms";
+    ASSERT_EQ(info.stage(), stage) << "Task lifecycle inconsistent";
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  Start
+//
+// ------------------------------------------------------------------------------------------------------
+
+Os::Test::Task::Tester::Start::Start() :
+        STest::Rule<Os::Test::Task::Tester>("Start") {
+}
+
+
+bool Os::Test::Task::Tester::Start::precondition(
+        const Os::Test::Task::Tester &state //!< The test state
+) {
+    return state.m_tasks.size() < Os::Test::Task::Tester::MAX_THREAD_COUNT;
+}
+
+
+void Os::Test::Task::Tester::Start::action(
+        Os::Test::Task::Tester &state //!< The test state
+) {
+    std::shared_ptr<TestTaskInfo> new_task = std::make_shared<TestTaskInfo>();
+    state.m_tasks.push_back(new_task);
+
+
+    Fw::String name("StartRuleTask");
+    new_task->start(&TestTaskInfo::standard_task);
+    ASSERT_EQ(state.m_last_task, &new_task->m_task) << "New task not registered";
+    // Poke the task into the MIDDLE state
+    new_task->signal();
+    wait_for_state_with_timeout(*new_task, TestTaskInfo::MIDDLE, 100);
+    ASSERT_EQ(Os::Task::getNumTasks(), TestTaskInfo::s_task_count) << "Task count miss-match";
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  Join
+//
+// ------------------------------------------------------------------------------------------------------
+
+Os::Test::Task::Tester::Join::Join() :
+        STest::Rule<Os::Test::Task::Tester>("Join") {
+}
+
+bool Os::Test::Task::Tester::Join::precondition(
+        const Os::Test::Task::Tester &state //!< The test state
+) {
+    return not state.m_tasks.empty();
+}
+
+
+void Os::Test::Task::Tester::Join::action(
+        Os::Test::Task::Tester &state //!< The test state
+) {
+    TestTaskInfo joiner_task;
+    const U32 random_index = STest::Pick::lowerUpper(0, state.m_tasks.size() - 1);
+
+    // Pop the middle
+    std::shared_ptr<TestTaskInfo> other_task = state.m_tasks[random_index];
+    state.m_tasks.erase(state.m_tasks.begin() + random_index);
+
+    // Set the other argument
+    joiner_task.m_other = other_task;
+
+    joiner_task.start(&TestTaskInfo::joining_task);
+
+    // Ensure it starts in the correct state for joining
+    ASSERT_EQ(other_task->stage(), TestTaskInfo::Lifecycle::MIDDLE);
+
+    // Poke the task into the MIDDLE state
+    joiner_task.signal();
+    wait_for_state_with_timeout(joiner_task, TestTaskInfo::MIDDLE, 100);
+
+    // Make the other task move from MIDDLE state to end state and wait for JOINED state
+    other_task->signal();
+    wait_for_state_with_timeout(joiner_task, TestTaskInfo::END, 100);
+    ASSERT_EQ(Os::Task::getNumTasks(), TestTaskInfo::s_task_count) << "Task count miss-match";
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  CheckState
+//
+// ------------------------------------------------------------------------------------------------------
+
+Os::Test::Task::Tester::CheckState::CheckState() :
+        STest::Rule<Os::Test::Task::Tester>("CheckState") {
+}
+
+
+bool Os::Test::Task::Tester::CheckState::precondition(
+        const Os::Test::Task::Tester &state //!< The test state
+) {
+    return true;
+}
+
+void Os::Test::Task::Tester::CheckState::action(
+        Os::Test::Task::Tester &state //!< The test state
+) {
+    std::shared_ptr<TestTaskInfo> task;
+    const U32 random_index = STest::Pick::lowerUpper(0, state.m_tasks.size());
+
+    // Allow small chance to check new task is in NOT_STARTED state
+    if (random_index >= state.m_tasks.size()) {
+        task = std::make_shared<TestTaskInfo>();
+    } else {
+        task = state.m_tasks[random_index];
+    }
+    ASSERT_EQ(task->m_task.getState(), task->m_state) << "Task state progression not as expected.";
+    ASSERT_EQ(Os::Task::getNumTasks(), TestTaskInfo::s_task_count) << "Task count miss-match";
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  Delay
+//
+// ------------------------------------------------------------------------------------------------------
+
+Os::Test::Task::Tester::Delay::Delay() :
+        STest::Rule<Os::Test::Task::Tester>("Delay") {
+}
+
+bool Os::Test::Task::Tester::Delay::precondition(
+        const Os::Test::Task::Tester &state //!< The test state
+) {
+    return true;
+}
+
+void Os::Test::Task::Tester::Delay::action(
+        Os::Test::Task::Tester &state //!< The test state
+) {
+    const U32 delay_micro_seconds = 5; //STest::Pick::lowerUpper(0, MAX_DELAY_MICRO_SECONDS);
+    Fw::Time delay(delay_micro_seconds / 1000000, delay_micro_seconds % 1000000);
+
+    timeval start;
+    timeval end;
+    ASSERT_EQ(gettimeofday(&start, nullptr), 0) << "Failed to get time";
+    Os::Task::delay(delay);
+    ASSERT_EQ(gettimeofday(&end, nullptr), 0) << "Failed to get time";
+
+    U32 wall_clock_delay_micro = ((end.tv_sec - start.tv_sec) * 1000000) + (end.tv_usec - start.tv_usec);
+    ASSERT_GE(wall_clock_delay_micro, delay_micro_seconds);
+    ASSERT_LT(wall_clock_delay_micro, delay_micro_seconds + 100000) << "Delay not within 100ms";
+    ASSERT_EQ(Os::Task::getNumTasks(), TestTaskInfo::s_task_count) << "Task count miss-match";
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  CheckTaskCount
+//
+// ------------------------------------------------------------------------------------------------------
+
+Os::Test::Task::Tester::CheckTaskCount::CheckTaskCount() :
+        STest::Rule<Os::Test::Task::Tester>("CheckTaskCount") {
+}
+
+bool Os::Test::Task::Tester::CheckTaskCount::precondition(
+        const Os::Test::Task::Tester &state //!< The test state
+) {
+    return true;
+}
+
+void Os::Test::Task::Tester::CheckTaskCount::action(
+        Os::Test::Task::Tester &state //!< The test state
+) {
+    FwSizeType count = TestTaskInfo::s_task_count;
+    ASSERT_EQ(Os::Task::getNumTasks(), count) << "Task count miss-match";
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  JoinInvalidState
+//
+// ------------------------------------------------------------------------------------------------------
+
+Os::Test::Task::Tester::JoinInvalidState::JoinInvalidState() :
+        STest::Rule<Os::Test::Task::Tester>("JoinInvalidState") {
+}
+
+
+bool Os::Test::Task::Tester::JoinInvalidState::precondition(
+        const Os::Test::Task::Tester &state //!< The test state
+) {
+    return true;
+}
+
+
+void Os::Test::Task::Tester::JoinInvalidState::action(
+        Os::Test::Task::Tester &state //!< The test state
+) {
+    std::shared_ptr<TestTaskInfo> new_task = std::make_shared<TestTaskInfo>();
+    ASSERT_NE(new_task->m_task.join(), Os::Task::Status::OP_OK);
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  StartIllegalRoutine
+//
+// ------------------------------------------------------------------------------------------------------
+
+Os::Test::Task::Tester::StartIllegalRoutine::StartIllegalRoutine() :
+        STest::Rule<Os::Test::Task::Tester>("StartIllegalRoutine") {
+}
+
+
+bool Os::Test::Task::Tester::StartIllegalRoutine::precondition(
+        const Os::Test::Task::Tester &state //!< The test state
+) {
+    return true;
+}
+
+
+void Os::Test::Task::Tester::StartIllegalRoutine::action(
+        Os::Test::Task::Tester &state //!< The test state
+) {
+    std::shared_ptr<TestTaskInfo> new_task = std::make_shared<TestTaskInfo>();
+    state.m_tasks.push_back(new_task);
+
+
+    Fw::String name("StartRuleTask");
+    ASSERT_DEATH(new_task->start(nullptr), "Os/Task\\.cpp:") << "Failed to trap NULL routine";
+}
+
diff --git a/Os/test/ut/task/TaskRules.hpp b/Os/test/ut/task/TaskRules.hpp
new file mode 100644
index 0000000000..dd64e20f95
--- /dev/null
+++ b/Os/test/ut/task/TaskRules.hpp
@@ -0,0 +1,250 @@
+
+
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  Start
+//
+// ------------------------------------------------------------------------------------------------------
+struct Start : public STest::Rule<Os::Test::Task::Tester> {
+
+    // ----------------------------------------------------------------------
+    // Construction
+    // ----------------------------------------------------------------------
+
+    //! Constructor
+    Start();
+
+    // ----------------------------------------------------------------------
+    // Public member functions
+    // ----------------------------------------------------------------------
+
+    //! Precondition
+    bool precondition(
+            const Os::Test::Task::Tester &state //!< The test state
+    );
+
+    //! Action
+    void action(
+            Os::Test::Task::Tester &state //!< The test state
+    );
+
+};
+
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  Join
+//
+// ------------------------------------------------------------------------------------------------------
+struct Join : public STest::Rule<Os::Test::Task::Tester> {
+
+    // ----------------------------------------------------------------------
+    // Construction
+    // ----------------------------------------------------------------------
+
+    //! Constructor
+    Join();
+
+    // ----------------------------------------------------------------------
+    // Public member functions
+    // ----------------------------------------------------------------------
+
+    //! Precondition
+    bool precondition(
+            const Os::Test::Task::Tester &state //!< The test state
+    );
+
+    //! Action
+    void action(
+            Os::Test::Task::Tester &state //!< The test state
+    );
+
+};
+
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  CheckState
+//
+// ------------------------------------------------------------------------------------------------------
+struct CheckState : public STest::Rule<Os::Test::Task::Tester> {
+
+    // ----------------------------------------------------------------------
+    // Construction
+    // ----------------------------------------------------------------------
+
+    //! Constructor
+    CheckState();
+
+    // ----------------------------------------------------------------------
+    // Public member functions
+    // ----------------------------------------------------------------------
+
+    //! Precondition
+    bool precondition(
+            const Os::Test::Task::Tester &state //!< The test state
+    );
+
+    //! Action
+    void action(
+            Os::Test::Task::Tester &state //!< The test state
+    );
+
+};
+
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  Delay
+//
+// ------------------------------------------------------------------------------------------------------
+struct Delay : public STest::Rule<Os::Test::Task::Tester> {
+
+    // ----------------------------------------------------------------------
+    // Construction
+    // ----------------------------------------------------------------------
+
+    //! Constructor
+    Delay();
+
+    // ----------------------------------------------------------------------
+    // Public member functions
+    // ----------------------------------------------------------------------
+
+    //! Precondition
+    bool precondition(
+            const Os::Test::Task::Tester &state //!< The test state
+    );
+
+    //! Action
+    void action(
+            Os::Test::Task::Tester &state //!< The test state
+    );
+
+};
+
+
+// ------------------------------------------------------------------------------------------------------
+// Rule:  CheckTaskCount
+//
+// ------------------------------------------------------------------------------------------------------
+struct CheckTaskCount : public STest::Rule<Os::Test::Task::Tester> {
+
+    // ----------------------------------------------------------------------
+    // Construction
+    // ----------------------------------------------------------------------
+
+    //! Constructor
+    CheckTaskCount();
+
+    // ----------------------------------------------------------------------
+    // Public member functions
+    // ----------------------------------------------------------------------
+
+    //! Precondition
+    bool precondition(
+            const Os::Test::Task::Tester &state //!< The test state
+    );
+
+    //! Action
+    void action(
+            Os::Test::Task::Tester &state //!< The test state
+    );
+
+};
+
+    
+
+
+
+    // ------------------------------------------------------------------------------------------------------
+    // Rule:  Registry
+    //
+    // ------------------------------------------------------------------------------------------------------
+    struct Registry : public STest::Rule<Os::Test::Task::Tester> {
+
+            // ----------------------------------------------------------------------
+            // Construction
+            // ----------------------------------------------------------------------
+
+            //! Constructor
+            Registry();
+
+            // ----------------------------------------------------------------------
+            // Public member functions
+            // ----------------------------------------------------------------------
+
+            //! Precondition
+            bool precondition(
+                const Os::Test::Task::Tester& state //!< The test state
+            );
+
+            //! Action
+            void action(
+                Os::Test::Task::Tester& state //!< The test state
+            );
+
+    };
+
+    
+
+
+
+    // ------------------------------------------------------------------------------------------------------
+    // Rule:  JoinInvalidState
+    //
+    // ------------------------------------------------------------------------------------------------------
+    struct JoinInvalidState : public STest::Rule<Os::Test::Task::Tester> {
+
+            // ----------------------------------------------------------------------
+            // Construction
+            // ----------------------------------------------------------------------
+
+            //! Constructor
+            JoinInvalidState();
+
+            // ----------------------------------------------------------------------
+            // Public member functions
+            // ----------------------------------------------------------------------
+
+            //! Precondition
+            bool precondition(
+                const Os::Test::Task::Tester& state //!< The test state
+            );
+
+            //! Action
+            void action(
+                Os::Test::Task::Tester& state //!< The test state
+            );
+
+    };
+
+    
+
+
+
+    // ------------------------------------------------------------------------------------------------------
+    // Rule:  StartIllegalRoutine
+    //
+    // ------------------------------------------------------------------------------------------------------
+    struct StartIllegalRoutine : public STest::Rule<Os::Test::Task::Tester> {
+
+            // ----------------------------------------------------------------------
+            // Construction
+            // ----------------------------------------------------------------------
+
+            //! Constructor
+            StartIllegalRoutine();
+
+            // ----------------------------------------------------------------------
+            // Public member functions
+            // ----------------------------------------------------------------------
+
+            //! Precondition
+            bool precondition(
+                const Os::Test::Task::Tester& state //!< The test state
+            );
+
+            //! Action
+            void action(
+                Os::Test::Task::Tester& state //!< The test state
+            );
+
+    };
diff --git a/Svc/CmdSequencer/test/ut/CmdSequencerTester.cpp b/Svc/CmdSequencer/test/ut/CmdSequencerTester.cpp
index cccea48610..3fa7e6584f 100644
--- a/Svc/CmdSequencer/test/ut/CmdSequencerTester.cpp
+++ b/Svc/CmdSequencer/test/ut/CmdSequencerTester.cpp
@@ -12,6 +12,7 @@
 #include "Svc/CmdSequencer/test/ut/CommandBuffers.hpp"
 #include "Svc/CmdSequencer/test/ut/SequenceFiles/FPrime/FPrime.hpp"
 #include "CmdSequencerTester.hpp"
+#include "Os/Delegate.hpp"
 
 namespace Svc {
 
@@ -816,26 +817,14 @@ namespace Svc {
 } // namespace Svc
 
 namespace Os {
-//! Overrides the default delegate function with this one as it is defined in the local compilation archive
-//! \param aligned_placement_new_memory: memory to fill
-//! \param to_copy: possible copy
-//! \return: new interceptor
-FileInterface *FileInterface::getDelegate(U8 *aligned_placement_new_memory, const FileInterface* to_copy) {
-    FW_ASSERT(aligned_placement_new_memory != nullptr);
-    const Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor* copy_me =
-            reinterpret_cast<const Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor*>(to_copy);
-    // Placement-new the file handle into the opaque file-handle storage
-    static_assert(sizeof(Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor) <= sizeof Os::File::m_handle_storage,
-            "Handle size not large enough");
-    static_assert((FW_HANDLE_ALIGNMENT % alignof(Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor)) == 0,
-            "Handle alignment invalid");
-    Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor *interface = nullptr;
-    if (to_copy == nullptr) {
-        interface = new(aligned_placement_new_memory) Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor;
-    } else {
-        interface = new(aligned_placement_new_memory) Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor(*copy_me);
-    }
-    FW_ASSERT(interface != nullptr);
-    return interface;
+
+//! \brief get a delegate for FileInterface that intercepts calls for command sequencer testing
+//! \param aligned_new_memory: aligned memory to fill
+//! \param to_copy: pointer to copy-constructor input
+//! \return: pointer to delegate
+FileInterface *FileInterface::getDelegate(HandleStorage& aligned_placement_new_memory, const FileInterface* to_copy) {
+    return Os::Delegate::makeDelegate<FileInterface, Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor>(
+            aligned_placement_new_memory, to_copy
+    );
 }
 }
diff --git a/Svc/PrmDb/test/ut/PrmDbImplTester.cpp b/Svc/PrmDb/test/ut/PrmDbImplTester.cpp
index 3fb56da69c..a3b33dd16d 100644
--- a/Svc/PrmDb/test/ut/PrmDbImplTester.cpp
+++ b/Svc/PrmDb/test/ut/PrmDbImplTester.cpp
@@ -9,6 +9,7 @@
 #include <Fw/Com/ComBuffer.hpp>
 #include <Fw/Com/ComPacket.hpp>
 #include <Os/Stub/test/File.hpp>
+#include <Os/Delegate.hpp>
 #include <cstdio>
 #include <gtest/gtest.h>
 
@@ -630,24 +631,14 @@ namespace Svc {
 } /* namespace Svc */
 
 namespace Os {
-//! Overrides the default delegate function with this one as it is defined in the local compilation archive
-//! \param aligned_placement_new_memory: memory to fill
-//! \param to_copy: possible copy
-//! \return: new interceptor
-FileInterface *FileInterface::getDelegate(U8 *aligned_placement_new_memory, const FileInterface* to_copy) {
-    FW_ASSERT(aligned_placement_new_memory != nullptr);
-    const Svc::PrmDbImplTester::PrmDbTestFile* copy_me =
-            reinterpret_cast<const Svc::PrmDbImplTester::PrmDbTestFile*>(to_copy);
-    // Placement-new the file handle into the opaque file-handle storage
-    static_assert(sizeof(Svc::PrmDbImplTester::PrmDbTestFile) <= FW_HANDLE_MAX_SIZE, "Handle size not large enough");
-    static_assert((FW_HANDLE_ALIGNMENT % alignof(Svc::PrmDbImplTester::PrmDbTestFile)) == 0, "Handle alignment invalid");
-    Svc::PrmDbImplTester::PrmDbTestFile *interface = nullptr;
-    if (to_copy == nullptr) {
-        interface = new(aligned_placement_new_memory) Svc::PrmDbImplTester::PrmDbTestFile;
-    } else {
-        interface = new(aligned_placement_new_memory) Svc::PrmDbImplTester::PrmDbTestFile(*copy_me);
-    }
-    FW_ASSERT(interface != nullptr);
-    return interface;
+
+//! \brief get a delegate for FileInterface that intercepts calls for parameter database testing
+//! \param aligned_new_memory: aligned memory to fill
+//! \param to_copy: pointer to copy-constructor input
+//! \return: pointer to delegate
+FileInterface *FileInterface::getDelegate(HandleStorage& aligned_placement_new_memory, const FileInterface* to_copy) {
+    return Os::Delegate::makeDelegate<FileInterface, Svc::PrmDbImplTester::PrmDbTestFile>(
+            aligned_placement_new_memory, to_copy
+    );
 }
 }
diff --git a/Utils/test/ut/LockGuardTester.cpp b/Utils/test/ut/LockGuardTester.cpp
index 69e644653c..b7d6eaf627 100644
--- a/Utils/test/ut/LockGuardTester.cpp
+++ b/Utils/test/ut/LockGuardTester.cpp
@@ -53,23 +53,24 @@ namespace Utils {
     TaskData data;
     data.i = 0;
     Os::Task testTask;
-    Os::Task::TaskStatus stat;
+    Os::Task::Status stat;
     Os::TaskString name("TestTask");
 
     {
       LockGuard guard(data.mutex);
-      stat = testTask.start(name, taskMethod, &data);
-      ASSERT_EQ(stat, Os::Task::TASK_OK);
-      Os::Task::delay(100);
+      Os::Task::Arguments arguments(name, taskMethod, &data);
+      stat = testTask.start(arguments);
+      ASSERT_EQ(stat, Os::Task::OP_OK);
+      Os::Task::delay(Fw::Time(0, 100));
       ASSERT_EQ(data.i, 0);
     }
-    Os::Task::delay(100);
+    Os::Task::delay(Fw::Time(0, 100));
     {
       LockGuard guard(data.mutex);
       ASSERT_EQ(data.i, 1);
     }
-    stat = testTask.join(nullptr);
-    ASSERT_EQ(stat, Os::Task::TASK_OK);
+    stat = testTask.join();
+    ASSERT_EQ(stat, Os::Task::OP_OK);
   }