diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..0773e96
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,23 @@
+# syntax=docker/dockerfile:1
+
+FROM oven/bun:1 AS base
+WORKDIR /app
+
+FROM base AS deps
+COPY bun.lock package.json ./
+RUN bun install --frozen-lockfile
+
+FROM deps AS build
+COPY . .
+RUN bun run build
+
+FROM base AS runtime
+ENV NODE_ENV=production
+COPY package.json bun.lock ./
+RUN bun install --frozen-lockfile --production
+
+COPY --from=build /app/.output ./.output
+
+EXPOSE 8085
+
+CMD ["bun", "run", "serve"]
diff --git a/app.config.ts b/app.config.ts
deleted file mode 100644
index 7e183c7..0000000
--- a/app.config.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { defineConfig } from "@solidjs/start/config";
-import tailwindcss from "@tailwindcss/vite";
-//@ts-expect-error
-import pkg from "@vinxi/plugin-mdx";
-import { blogPostsPlugin } from "./build-helpers/blogPostsPlugin";
-import remarkFrontmatter from "remark-frontmatter";
-import rehypeMdxCodeProps from "rehype-mdx-code-props";
-import { mdxPrism } from "./build-helpers/mdxPrism";
-import remarkToc from "remark-toc";
-
-const { default: mdx } = pkg;
-export default defineConfig({
- extensions: ["mdx", "md"],
- vite: {
- plugins: [
- tailwindcss(),
- mdx.withImports({})({
- remarkPlugins: [remarkFrontmatter, remarkToc],
- rehypePlugins: [rehypeMdxCodeProps, mdxPrism],
- jsx: true,
- jsxImportSource: "solid-js",
- providerImportSource: "solid-mdx",
- }),
- blogPostsPlugin(),
- ],
- build: {
- target: "esnext",
- },
- },
- server: {
- prerender: {
- crawlLinks: true,
- },
- },
-});
diff --git a/public/resume.pdf b/public/resume.pdf
index 67d8fba..25f238f 100644
Binary files a/public/resume.pdf and b/public/resume.pdf differ
diff --git a/src/components/Experience.jsx b/src/components/Experience.jsx
index 8dae0bf..163b151 100644
--- a/src/components/Experience.jsx
+++ b/src/components/Experience.jsx
@@ -6,10 +6,10 @@ const Experience = () => {
const [jobs, _] = createSignal([
{
title: "Software Developer Engineer Intern",
- company: "Amazon Canada (Delivery Extensions Team)",
+ company: "Amazon Canada",
location: "Toronto, Ontario, Canada",
- range: "May 2025 - July 2025",
- url: "https://amazon.jobs/content/en/teams/ftr",
+ range: "May 2025 - August 2025",
+ url: "https://flex.amazon.ca/",
},
{
title: "Software Engineer Intern",
diff --git a/src/data/posts.json b/src/data/posts.json
index 522c81a..4229a8a 100644
--- a/src/data/posts.json
+++ b/src/data/posts.json
@@ -1,4 +1,13 @@
[
+ {
+ "title": "A Rusty Stack Jump",
+ "description": "Jumping into a new stack with Rust",
+ "date": "2025-02-27",
+ "featuredImage": null,
+ "featuredImageDesc": null,
+ "tags": ["rust", "asm", "systems", "operating systems", "async"],
+ "slug": "rust_stack_jmp"
+ },
{
"title": "Testing Test Test Test",
"description": "Woah this is so cool",
diff --git a/src/routes/blog/rust_stack_jmp.mdx b/src/routes/blog/rust_stack_jmp.mdx
new file mode 100644
index 0000000..7029081
--- /dev/null
+++ b/src/routes/blog/rust_stack_jmp.mdx
@@ -0,0 +1,129 @@
+---
+title: A Rusty Stack Jump
+description: Jumping into a new stack with Rust
+date: 2025-02-27
+featuredImage:
+featuredImageDesc:
+tags:
+ - rust
+ - asm
+ - systems
+ - operating systems
+ - async
+---
+
+import { Notes, PostImage } from "~/components/Markdown";
+import { Tree } from "~/components/Tree";
+
+In my quest to learn to build an async runtime in Rust, I have to learn about CPU context switching. In order to switch from one async task to another, our async runtime has to perform a context switch. This means saving the current CPU registers marked as `callee saved` by the System V ABI manual and loading the CPU registers with our new async stack.
+
+In this article, I will show you what I have learned about jumping onto a new stack in a x86_64 CPU.
+
+
+I'm learning about async runtimes in Rust based on the amazing book [Asynchronous Programming in Rust: Learn asynchronous programming by building working examples of futures, green threads, and runtimes](https://www.packtpub.com/en-mt/product/asynchronous-programming-in-rust-9781805128137)
+
+It's an amazing book, don't get me wrong, but I feel like the explanation can be hand-wavy sometimes. Thus, I write this to archive my own explanation and potentially help other people who also struggle with the subject.
+
+
+
+
+ Most async runtimes in Rust do not use stackful coroutines (which are used by
+ Go's `gochannel`, Erlang's `processes`) and instead, use state machines to
+ manage async tasks.
+
+
+## Contents
+
+
+
+## Setting the stage
+
+Why do we need to swap the stack of async tasks in a runtime with stackful coroutines ?
+
+Async tasks, by nature, are paused and resumed. Everytime a task is paused to move into a new task, we would have to save the current context of the task that is running and load the context of the upcoming task.
+
+## Jumping into the new stack
+
+Here is the code in its entirely, I'd recommend you run this on the [Rust Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024). I have left comments through out the code so you can get the general idea.
+
+Note that you have to manually stop the process.
+
+```rust lang="rust" file="stack_swap.rs"
+use core::arch::asm;
+
+// stack size of 48 bytes so its easy to print the stack before we switch contexts
+const SSIZE: isize = 48;
+
+// a struct that represents our CPU state
+//
+// This struct will stores the stack pointer
+#[derive(Debug, Default)]
+#[repr(C)]
+struct ThreadContext {
+ rsp: u64,
+}
+
+// Returning ! means
+// it will panic OR runs forever
+fn hello() -> ! {
+ println!("I LOVE WAKING UP ON A NEW STACK!");
+ loop {}
+}
+
+// new is a pointer to a ThreadContext
+unsafe fn gt_switch(new: *const ThreadContext) {
+ // inline assembly
+ asm!(
+ "mov rsp, [{0} + 0x00]", // move the content of where the new pointer is pointing to, into the rsp register
+ "ret", // ret pops the return address from our custom stackāin our example, the address of hello.
+ in(reg) new,
+ );
+}
+
+fn main() {
+ // initialize
+ let mut ctx = ThreadContext::default();
+
+ // stack initialize
+ // ie. 0x10
+ let mut stack = vec![0_u8; SSIZE as usize];
+
+ unsafe {
+ // we get the bottom of the stack
+ // remember that the stack grows downward from high memory address to low memory address
+ // i.e 0x40 -> because 0x30 = 0x40 - 0x10 and 0x30 = SSIZE in decimal
+ // NOTE: offset() is applied in units of the size of the type that the pointer points to
+ // in our case, stack is a pointer to u8 (a byte) so offset(SSIZE) == offset(48 bytes) == offset(0x30)
+ let stack_bottom = stack.as_mut_ptr().offset(SSIZE);
+
+ // we align the bottom of the stack to be 16-byte-aligned
+ // this is for performance reasons as some CPU instructions (SSE and SIMD)
+
+ // The technicality: 15 is b1111 so if we do (stack_bottom AND !15) we will zero out the bottom 4 bits
+ //
+ // we also want the bottom of the stack pointer to point to a byte (8bit or u8)
+ let sb_aligned = (stack_bottom as usize & !15) as *mut u8;
+
+ // Here, we write the address of the hello function as 64 bits(8 bytes)
+ // Remember that 16 bytes = 0x10 in hex
+ // So we go DOWN 10 memory addresses, i.e from 0x40 to 0x30
+ // NOTE: 16 bytes down (0x10) even though, the hello function pointer is ONLY 8 bytes
+ // This is because the System V ABI requires the stack pointer to be always be 16-byte aligned
+ std::ptr::write(sb_aligned.offset(-16) as *mut u64, hello as u64);
+
+ // we write the stack pointer into the rsp inside context
+ ctx.rsp = sb_aligned.offset(-16) as u64;
+
+ for i in 0..SSIZE {
+ println!("mem: {}, val: {}",
+ sb_aligned.offset(-i as isize) as usize,
+ *sb_aligned.offset(-i as isize))
+ };
+
+ // we go into the function
+ // we will write our stack pointer to the cpu stack pointer
+ // and `ret` will pop that stack pointer
+ gt_switch(&mut ctx);
+ }
+}
+```
diff --git a/src/routes/tags/[id].tsx b/src/routes/tags/[id].tsx
index 7ba782b..0b9b69a 100644
--- a/src/routes/tags/[id].tsx
+++ b/src/routes/tags/[id].tsx
@@ -3,6 +3,7 @@ import { type Component, Show } from "solid-js";
import { posts } from "~/data/posts";
import { Posts } from "~/components/Posts";
import { tags } from "~/data/tags";
+import { Title, Meta } from "@solidjs/meta";
/*
* Code from andii.dev
@@ -11,6 +12,18 @@ const TagId: Component> = (props) => {
const tag = () => tags[props.params.id];
return (
No posts with that tag}>
+ minhtran_dev - Tag: {tag().id}
+
+
+
+
+
+
+
+
Tag: {tag().id}