feat(meta): update meta info for tag ids
This commit is contained in:
23
Dockerfile
Normal file
23
Dockerfile
Normal file
@@ -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"]
|
||||||
@@ -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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Binary file not shown.
@@ -6,10 +6,10 @@ const Experience = () => {
|
|||||||
const [jobs, _] = createSignal([
|
const [jobs, _] = createSignal([
|
||||||
{
|
{
|
||||||
title: "Software Developer Engineer Intern",
|
title: "Software Developer Engineer Intern",
|
||||||
company: "Amazon Canada (Delivery Extensions Team)",
|
company: "Amazon Canada",
|
||||||
location: "Toronto, Ontario, Canada",
|
location: "Toronto, Ontario, Canada",
|
||||||
range: "May 2025 - July 2025",
|
range: "May 2025 - August 2025",
|
||||||
url: "https://amazon.jobs/content/en/teams/ftr",
|
url: "https://flex.amazon.ca/",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Software Engineer Intern",
|
title: "Software Engineer Intern",
|
||||||
|
|||||||
@@ -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",
|
"title": "Testing Test Test Test",
|
||||||
"description": "Woah this is so cool",
|
"description": "Woah this is so cool",
|
||||||
|
|||||||
129
src/routes/blog/rust_stack_jmp.mdx
Normal file
129
src/routes/blog/rust_stack_jmp.mdx
Normal file
@@ -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.
|
||||||
|
|
||||||
|
<Notes>
|
||||||
|
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.
|
||||||
|
|
||||||
|
</Notes>
|
||||||
|
|
||||||
|
<Notes>
|
||||||
|
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.
|
||||||
|
</Notes>
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
## 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -3,6 +3,7 @@ import { type Component, Show } from "solid-js";
|
|||||||
import { posts } from "~/data/posts";
|
import { posts } from "~/data/posts";
|
||||||
import { Posts } from "~/components/Posts";
|
import { Posts } from "~/components/Posts";
|
||||||
import { tags } from "~/data/tags";
|
import { tags } from "~/data/tags";
|
||||||
|
import { Title, Meta } from "@solidjs/meta";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Code from andii.dev
|
* Code from andii.dev
|
||||||
@@ -11,6 +12,18 @@ const TagId: Component<RouteSectionProps<unknown>> = (props) => {
|
|||||||
const tag = () => tags[props.params.id];
|
const tag = () => tags[props.params.id];
|
||||||
return (
|
return (
|
||||||
<Show when={tag()} fallback={<div>No posts with that tag</div>}>
|
<Show when={tag()} fallback={<div>No posts with that tag</div>}>
|
||||||
|
<Title>minhtran_dev - Tag: {tag().id}</Title>
|
||||||
|
|
||||||
|
<Meta
|
||||||
|
property="og:title"
|
||||||
|
content={`minhtran_dev Articles Tag: ${tag().id}`}
|
||||||
|
/>
|
||||||
|
<Meta property="og:description" content={`My ${tag().id} Articles`} />
|
||||||
|
<Meta property="og:image" content="/og.png" />
|
||||||
|
<Meta property="og:image:alt" content="minhtran_dev site" />
|
||||||
|
<Meta property="og:image:width" content="1200" />
|
||||||
|
<Meta property="og:image:height" content="630" />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-lg font-bold mb-6">Tag: {tag().id}</h1>
|
<h1 class="text-lg font-bold mb-6">Tag: {tag().id}</h1>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user