diff --git a/Cargo.lock b/Cargo.lock
index 01b48c7..1d30499 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -203,6 +203,25 @@ name = "cpuio"
 version = "0.3.2"
 source = "git+https://git.ablecorp.us/ondra05/cpuio.git#093cc103101b4ba4abd02d77c884113a376cdc64"
 
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "downcast-rs"
 version = "1.2.0"
@@ -295,8 +314,10 @@ dependencies = [
 name = "kernel"
 version = "0.1.2"
 dependencies = [
+ "crossbeam-queue",
  "linked_list_allocator",
  "log",
+ "slab",
  "versioning",
 ]
 
@@ -647,6 +668,15 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "slab"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+dependencies = [
+ "autocfg",
+]
+
 [[package]]
 name = "spin"
 version = "0.5.2"
diff --git a/ableos/src/arch/x86_64/interrupts.rs b/ableos/src/arch/x86_64/interrupts.rs
index 9f63f2e..7ae3d3e 100644
--- a/ableos/src/arch/x86_64/interrupts.rs
+++ b/ableos/src/arch/x86_64/interrupts.rs
@@ -98,34 +98,11 @@ extern "x86-interrupt" fn double_fault_handler(
 * As long as nothing in this function does something that would
 * normally trigger an error, this function is relatively safe.
 */
-#[naked]
 extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
-    use super::task_switcher;
+    crate::kmain::tick();
     unsafe {
-        asm!(
-
-
-            // "call {disable}",
-            // Kernel tick
-            "call {tick}",
-
-            // Push task's state onto stack
-            // and save task pointer into scheduler
-            task_switcher::save_tasks_state!(),
-            "mov rdi, rsp",
-            "call {save}",
-
-            // "call {enable}",
-            // Switch to next task (interrupt'll be returned there)
-            "jmp {switch_to_next}",
-
-            tick = sym crate::kmain::tick,
-            save = sym task_switcher::save_and_enqueue,
-            // disable = sym x86_64::instructions::interrupts::disable,
-            // enable = sym x86_64::instructions::interrupts::enable,
-            switch_to_next = sym task_switcher::switch_to_next,
-            options(noreturn),
-        );
+        PICS.lock()
+            .notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
     }
 }
 
@@ -192,7 +169,7 @@ pub fn init_idt() {
 }
 
 /// https://wiki.osdev.org/Pit
-/// 
+///
 const PIT_MAX_FREQ: u32 = 1193182;
 
 pub fn set_pit_frequency(pit: u16, freq: u32) {
diff --git a/ableos/src/arch/x86_64/mod.rs b/ableos/src/arch/x86_64/mod.rs
index 6ebf783..ce008fa 100644
--- a/ableos/src/arch/x86_64/mod.rs
+++ b/ableos/src/arch/x86_64/mod.rs
@@ -10,8 +10,6 @@ pub mod init;
 pub mod interrupts;
 pub mod memory;
 
-mod task_switcher;
-
 use crate::arch::drivers::allocator;
 use bootloader::{entry_point, BootInfo};
 use x86_64::{instructions::hlt, VirtAddr};
diff --git a/ableos/src/arch/x86_64/task_switcher.rs b/ableos/src/arch/x86_64/task_switcher.rs
deleted file mode 100644
index eb0ea9d..0000000
--- a/ableos/src/arch/x86_64/task_switcher.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-use crate::scheduler::{Task, SCHEDULER};
-
-/// Saves task's state onto stack
-macro_rules! save_tasks_state {
-    () => {
-        "
-            // Copy task's state
-            push [rsp + 32] // SS
-            push [rsp + 32] // RSP
-            push [rsp + 32] // RFLAGS
-            push [rsp + 32] // CS
-            push [rsp + 32] // RIP
-
-            // Save task's registers
-            push r15
-            push r14
-            push r13
-            push r12
-            push r11
-            push r10
-            push r9
-            push r8
-            push rdi
-            push rsi
-            push rdx
-            push rcx
-            push rbx
-            push rax
-        "
-    };
-}
-
-pub(super) use save_tasks_state;
-
-/// Save provided stack pointer into scheduler's queue
-pub extern "C" fn save_and_enqueue(sp: u64) {
-    SCHEDULER.lock().enqueue_suspended(sp);
-}
-
-// Fetch and load next task
-pub unsafe extern "C" fn switch_to_next() -> ! {
-    // Fetch next task
-    let next = SCHEDULER.lock().pop().expect("no task in the task queue");
-    match next {
-        Task::Suspended(sp) => asm!(
-            // Restore task's registers
-            "mov rsp, {}",
-            "pop rax",
-            "pop rbx",
-            "pop rcx",
-            "pop rdx",
-            "pop rsi",
-            "pop rdi",
-            "pop r8",
-            "pop r9",
-            "pop r10",
-            "pop r11",
-            "pop r12",
-            "pop r13",
-            "pop r14",
-            "pop r15",
-
-            // Copy things up the stack
-            "add rsp, 80", // Move 8 bytes above exception stack frame
-            "push [rsp - 48]", // SS
-            "push [rsp - 48]", // RSP
-            "push [rsp - 48]", // RFLAGS
-            "push [rsp - 48]", // CS
-            "push [rsp - 48]", // RIP
-
-            // Signalise end of the interrupt and return
-            "push rax",
-            "mov al, 32",
-            "out 20h, al",
-            "pop rax",
-            "iretq",
-
-            in(reg) sp,
-            options(noreturn),
-        ),
-        Task::Spawn {
-            function,
-            stack_start,
-        } => asm!(
-            "add rsp, 160", // Move above saved registries
-            "mov [rsp], {ip}", // Set task's instruction pointer
-            "mov [rsp + 24], {sp}", // Set task's stack pointer
-
-            // Signalise the end of the interrupt and return
-            "mov al, 32",
-            "out 20h, al",
-            "iretq",
-
-            ip = in(reg) function,
-            sp = in(reg) stack_start,
-            options(noreturn),
-        ),
-    }
-}
diff --git a/ableos/src/kmain.rs b/ableos/src/kmain.rs
index 195ed83..9a4cc1d 100644
--- a/ableos/src/kmain.rs
+++ b/ableos/src/kmain.rs
@@ -6,7 +6,7 @@ use crate::arch::{drivers::sysinfo::master, init, sloop};
 use crate::hardware;
 use crate::relib::network::socket::{SimpleSock, Socket};
 use crate::{
-    boot_conf::KernelConfig, scheduler::SCHEDULER, scratchpad, systeminfo::RELEASE_TYPE, TERM,
+    boot_conf::KernelConfig, scratchpad, systeminfo::RELEASE_TYPE, TERM,
 };
 use kernel::KERNEL_VERSION;
 use spin::Lazy;
@@ -66,16 +66,7 @@ pub fn kernel_main() -> ! {
     /*
     log_version_data();
     // */
-    x86_64::instructions::interrupts::without_interrupts(|| {
-        let mut scheduler = SCHEDULER.lock();
-        // comment this out to resume normal use
-        // scheduler.enqueue_spawn(traceloop);
-
-        scheduler.enqueue_spawn(scratchpad);
-    });
-
-    // scratchpad();
-
+    scratchpad();
     sloop()
 }
 
diff --git a/ableos/src/lib.rs b/ableos/src/lib.rs
index 97f12a6..2ab6915 100644
--- a/ableos/src/lib.rs
+++ b/ableos/src/lib.rs
@@ -66,7 +66,6 @@ pub mod prelude;
 pub mod ps2_mouse;
 pub mod relib;
 pub mod rhai_shell;
-pub mod scheduler;
 pub mod scratchpad;
 pub mod stdio;
 pub mod time;
diff --git a/ableos/src/scheduler.rs b/ableos/src/scheduler.rs
deleted file mode 100644
index ea70991..0000000
--- a/ableos/src/scheduler.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-use alloc::collections::VecDeque;
-use spin::{Lazy, Mutex};
-
-pub static SCHEDULER: Lazy<Mutex<Scheduler>> = Lazy::new(|| Mutex::new(Scheduler::default()));
-
-/// Task's stack size (in kiB)
-const STACK_SIZE: usize = 512;
-
-pub enum Task {
-    Suspended(u64),
-    Spawn { function: fn(), stack_start: usize },
-}
-
-#[derive(Default)]
-pub struct Scheduler {
-    task_queue: VecDeque<Task>,
-}
-
-impl Scheduler {
-    pub fn enqueue_spawn(&mut self, function: fn()) {
-        self.task_queue.push_back(Task::Spawn {
-            function,
-            stack_start: unsafe {
-                let size = STACK_SIZE * 1024;
-                alloc::alloc::alloc(
-                    alloc::alloc::Layout::from_size_align(size, 1).expect("invalid layout"),
-                )
-                .add(size - 1) as _
-            },
-        });
-    }
-
-    pub fn enqueue_suspended(&mut self, sp: u64) {
-        self.task_queue.push_back(Task::Suspended(sp));
-    }
-
-    pub fn pop(&mut self) -> Option<Task> {
-        self.task_queue.pop_front()
-    }
-}
-
-pub enum WakeConditions {
-    Time,
-    ReceivedMessageInChannel,
-    GainedAccessToChannel,
-}
diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml
index 8a86d74..d2c0d1c 100644
--- a/kernel/Cargo.toml
+++ b/kernel/Cargo.toml
@@ -6,6 +6,12 @@ version = "0.1.2"
 [dependencies]
 linked_list_allocator = "0.9"
 log = "0.4.14"
+slab = { version = "0.4", default-features = false }
+
+[dependencies.crossbeam-queue]
+version = "0.3"
+default-features = false
+features = ["alloc"]
 
 [dependencies.versioning]
 git = "https://git.ablecorp.us/able/aos_userland"
diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs
index 9b89327..e354bda 100644
--- a/kernel/src/lib.rs
+++ b/kernel/src/lib.rs
@@ -16,6 +16,7 @@ pub mod messaging;
 // pub mod panic;
 pub mod proccess;
 pub mod syscalls;
+pub mod task;
 pub mod time;
 
 use core::arch::asm;
diff --git a/kernel/src/task.rs b/kernel/src/task.rs
new file mode 100644
index 0000000..39f26d1
--- /dev/null
+++ b/kernel/src/task.rs
@@ -0,0 +1,101 @@
+//! Async task and executor
+
+use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, task::Wake};
+use core::{
+    future::Future,
+    pin::Pin,
+    task::{Context, Poll, Waker},
+};
+use crossbeam_queue::SegQueue;
+use slab::Slab;
+
+type TaskQueue = Arc<SegQueue<TaskId>>;
+
+/// Tasks executor
+#[derive(Default)]
+pub struct Executor {
+    /// All spawned tasks' stash
+    tasks: Slab<Task>,
+
+    /// Awake tasks' queue
+    queue: TaskQueue,
+
+    /// Wakers
+    wakers: BTreeMap<TaskId, Waker>,
+}
+
+impl Executor {
+    /// Spawn a future
+    pub fn spawn(&mut self, future: impl Future<Output = ()> + 'static) {
+        self.queue
+            .push(TaskId(self.tasks.insert(Task::new(future))));
+    }
+
+    /// Run tasks
+    pub fn run(&mut self) -> ! {
+        loop {
+            while let Some(id) = self.queue.pop() {
+                let task = match self.tasks.get_mut(id.0) {
+                    Some(t) => t,
+                    None => continue,
+                };
+
+                let mut cx = Context::from_waker(
+                    self.wakers
+                        .entry(id)
+                        .or_insert_with(|| TaskWaker::new(id, Arc::clone(&self.queue))),
+                );
+
+                match task.poll(&mut cx) {
+                    Poll::Ready(()) => {
+                        self.tasks.remove(id.0);
+                        self.wakers.remove(&id);
+                    }
+                    Poll::Pending => (),
+                }
+            }
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+struct TaskId(usize);
+
+/// Async task
+struct Task {
+    future: Pin<Box<dyn Future<Output = ()>>>,
+}
+
+impl Task {
+    /// Create a new task from a future
+    fn new(future: impl Future<Output = ()> + 'static) -> Self {
+        Self {
+            future: Box::pin(future),
+        }
+    }
+
+    fn poll(&mut self, cx: &mut Context) -> Poll<()> {
+        self.future.as_mut().poll(cx)
+    }
+}
+
+struct TaskWaker {
+    id: TaskId,
+    queue: TaskQueue,
+}
+
+impl TaskWaker {
+    fn new(id: TaskId, queue: TaskQueue) -> Waker {
+        Waker::from(Arc::new(Self { id, queue }))
+    }
+}
+
+impl Wake for TaskWaker {
+    fn wake(self: Arc<Self>) {
+        self.wake_by_ref();
+    }
+
+    fn wake_by_ref(self: &Arc<Self>) {
+        self.queue.push(self.id);
+    }
+}
diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs
index b8dc610..2deb779 100644
--- a/repbuild/src/main.rs
+++ b/repbuild/src/main.rs
@@ -29,8 +29,8 @@ impl Subcommand {
       match str.as_ref() {
          "doc" => Subcommand::Doc,
          "help" => Subcommand::Help,
-         "run" => Subcommand::Run,
-         "test" => Subcommand::Test,
+         "run" | "r" => Subcommand::Run,
+         "test" | "t" => Subcommand::Test,
          "" => Subcommand::Empty,
          unknown => Subcommand::Unknown(unknown.to_string()),
       }