Skip to content
Open

sync #46

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4d93a75
chore: Add logging for RSS memory in 01_system_info.c
ilianaryshkin Jul 6, 2024
48231fd
feat: libuv idle handler
ilianaryshkin Jul 7, 2024
7c62670
03_fs_readsync
ilianaryshkin Jul 9, 2024
80e2836
04_fs_readasync
ilianaryshkin Jul 10, 2024
8a9d163
05_fs_readasync_context
ilianaryshkin Jul 11, 2024
d09a546
06_fs_allasync
ilianaryshkin Jul 17, 2024
606d461
07_tcp_echo_server
ilianaryshkin Jul 22, 2024
f3befa0
08_horse_race
ilianaryshkin Jul 22, 2024
0e7f06c
interactive_horse_race
ilianaryshkin Jul 22, 2024
c7dead0
00_libuv_sandbox init
ilianaryshkin Jul 22, 2024
76ecd6a
epam_workshop init
ilianaryshkin Jul 24, 2024
839aa4d
Settled TODOs
ilianaryshkin Jul 24, 2024
d5145c2
libuv_sandbox - IDLE
ilianaryshkin Jul 24, 2024
729f84c
libuv_sandbox - Filesystem
ilianaryshkin Jul 25, 2024
e5154a8
libuv_sandbox - Network
ilianaryshkin Jul 25, 2024
5d9024c
demultiplexer
ilianaryshkin Jul 28, 2024
c655d0b
libuv
ilianaryshkin Jul 30, 2024
4aafcff
timers, intervals, Immediate, next tick
ilianaryshkin Jul 31, 2024
a4899a3
PromiseVsImmediate
ilianaryshkin Aug 1, 2024
14994cb
libevent
ilianaryshkin Aug 2, 2024
af03142
Libuv Event Loop Phases
ilianaryshkin Aug 5, 2024
3ee835a
Threadpool
ilianaryshkin Aug 6, 2024
26c500e
Best Practices
ilianaryshkin Aug 8, 2024
0f32851
restructure: update README and move JS sandbox to nodejs/
linnienaryshkin Mar 31, 2026
3e1ddf9
move: event_loop_sandbox/index.js to nodejs/
linnienaryshkin Mar 31, 2026
eb67432
Merge pull request #1 from linnienaryshkin/claude/update-readme-event…
linnienaryshkin Apr 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.vscode
.idea
.history

lib-cov
*.seed
*.log
Expand All @@ -17,3 +21,5 @@ deps
build
out
*.xcodeproj

.DS_Store
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.7.18
14 changes: 14 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"files.associations": {
"*.gz": "json",
"*.sql": "sql",
".*yml": "yaml",
"locale": "c",
"ios": "c",
"array": "c",
"string": "c",
"string_view": "c",
"vector": "c",
"learnuv.h": "c"
}
}
108 changes: 57 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,90 @@
# learnuv
# experiment-libuv

Learn uv for fun and profit, a self guided workshop to [the library](https://github.com/libuv/libuv) that powers Node.js.
This is a fork of [thlorenz/learnuv](https://github.com/thlorenz/learnuv), where I did my own exploration.

![screenshot](assets/screenshot.png)
## How it started

## Before You Start
It all started with this article series: [Event Loop and the Big Picture — NodeJS Event Loop Part 1](https://blog.insiderattack.net/event-loop-and-the-big-picture-nodejs-event-loop-part-1-1cb67a182810).

Read the [learnuv gitbook](http://thlorenz.github.io/learnuv/book) which explains some libuv and related C language
concepts that will help you complete the exercises.
The articles explain how the Node.js event loop works under the hood, and how it maps to libuv phases. I wanted to go deeper — not just read about it, but actually run libuv locally and see it in action.

## Installation
So I forked `learnuv` and worked through the C exercises to understand the raw event loop: timers, I/O polling, idle handlers, check handlers, and close handlers.

Requires [Python](https://www.python.org/downloads/) 2.6 or 2.7 to be installed.
If python is not in your path set the environment variable `PYTHON` to its
location. For example on Windows: `set PYTHON=C:\Python27\python.exe`
Once I had a feel for the libuv internals, I went back to Node.js and wrote [`nodejs/event_loop_sandbox/index.js`](nodejs/event_loop_sandbox/index.js) — a JavaScript sandbox that maps each libuv concept onto the corresponding Node.js APIs (`setTimeout`, `setImmediate`, `process.nextTick`, Promises, etc.).

```sh
git clone https://github.com/thlorenz/learnuv && cd learnuv
npm install
```

## Getting Started
## Repository structure

```sh
./learnuv help
```
src/ # C exercises using libuv (forked from learnuv)
01_system_info.c
02_idle.c
03_fs_readsync.c
04_fs_readasync.c
05_fs_readasync_context.c
06_fs_allasync.c
07_tcp_echo_server.c
08_horse_race.c
event_loop_sandbox/ # libuv event loop diagrams
libuv-event-loop.png
node-event-loop.png
...
nodejs/
event_loop_sandbox/
index.js # Node.js sandbox mapping libuv phases to JS APIs
```

## Building

**learnuv** comes with build commands that you can use instead of the manual steps explained further below.

- **./learnuv ninja**
Compile your sources with ninja, stored inside `./out/Debug`
- **./learnuv make**
Compile your sources with make, stored inside `./out/Debug`
- **./learnuv xcode**
Generate a learnuv Xcode project, named `./learnuv.xcodeproj`

### Windows Caveats
## Before You Start (C side)

Ninja and Make do not work on windows except via [cygwin](https://www.cygwin.com/).
1. C Introduction — https://www.w3schools.com/c/c_intro.php
2. Pointers — https://www.w3schools.com/c/c_pointers.php
3. Address-Of / Dereference
4. Struct — https://www.w3schools.com/c/c_structs.php
5. Automatic Allocation vs Dynamic Allocation (malloc)
6. NULL
7. Typecast — https://www.tutorialspoint.com/cprogramming/c_type_casting.htm
8. LibUV guidebook (5 mins reading) — https://thlorenz.com/learnuv/book/

### Ninja
And the official [LibUV documentation](https://docs.libuv.org/en/v1.x/guide/introduction.html).

Highly recommended since it builds faster than any of the other options, so [get ninja](https://ninja-build.org/)
and do:
## Setup for `macOS 14.5 23F79`

```sh
./gyp_learnuv.py -f ninja
ninja -C out/Debug
./out/Debug/01_system_info
npm install
```

### Make
```sh
pyenv install 2.7 &&
pyenv local 2.7.18
```

Works on Linux and OSX.
```sh
pip install six # https://github.com/thlorenz/learnuv/issues/45
```

```sh
./gyp_learnuv.py -f make
make -C out
./out/Debug/01_system_info
eval "$(pyenv init -)" # for some reason it's not working from ~/.zshrc
```

### Xcode
```sh
./learnuv
```

Works on OSX only. Highly recommended to ease debugging and code navigation.
```sh
./learnuv xcode
```

```sh
./gyp_learnuv.py -f xcode
open learnuv.xcodeproj
```

### Visual Studio

Works on Windows only. **TODO** need to adapt [vcbuild.bat](https://github.com/joyent/libuv/blob/master/vcbuild.bat).
```sh
./learnuv verify
```

## Running
## Running the Node.js sandbox

```
./learnuv
```sh
node nodejs/event_loop_sandbox
```

## License
Expand Down
Binary file removed assets/screenshot.png
Binary file not shown.
102 changes: 57 additions & 45 deletions exercises/07_tcp_echo_server/exercise.js
Original file line number Diff line number Diff line change
@@ -1,86 +1,98 @@
'use strict';
var net = require('net')
, colors = require('ansicolors')
, format = require('util').format
, table = require('text-table')
"use strict";
var net = require("net"),
colors = require("ansicolors"),
format = require("util").format,
table = require("text-table");

var HOST = '0.0.0.0'
, PORT = 7000;
var HOST = "0.0.0.0",
PORT = 7001;

var exercise = require('workshopper-exercise')()
var exercise = require("workshopper-exercise")();

exercise.requireSubmission = false;
exercise.addSetup(checkClientResponse);
exercise.addSetup(checkClientResponse);

module.exports = exercise;

function log() {
console.log(colors.green('progress ') + colors.brightBlack(format.apply(this, arguments)));
console.log(
colors.green("progress ") +
colors.brightBlack(format.apply(this, arguments))
);
}

function checkClientResponse(mode, cb) {
var progress = {
connected : false
, responded : false
, correctResponse : false
, closed : false
, error : false
}
connected: false,
responded: false,
correctResponse: false,
closed: false,
error: false,
};

var fail = false
, quit = false
, finished = false
, msg = 'Hello Server can you echo?'
, timeout
var fail = false,
quit = false,
finished = false,
msg = "Hello Server can you echo?",
timeout;

function finish() {
clearTimeout(timeout);
if (finished) return;
finished = true;

var arr = Object.keys(progress)
.reduce(function key(acc, k) {
if (k === 'error') { fail = fail || !!progress[k]; return acc }
fail = fail || !progress[k]
var label = colors.blue(k[0].toUpperCase() + k.slice(1));
acc.push([ label, progress[k] ? colors.green('OK') : colors.red('NOT OK') ])
return acc
}, [])
var arr = Object.keys(progress).reduce(function key(acc, k) {
if (k === "error") {
fail = fail || !!progress[k];
return acc;
}
fail = fail || !progress[k];
var label = colors.blue(k[0].toUpperCase() + k.slice(1));
acc.push([
label,
progress[k] ? colors.green("OK") : colors.red("NOT OK"),
]);
return acc;
}, []);

console.log("\n" + table(arr));

console.log('\n' + table(arr));

if (progress.error) console.error('\nEncountered an error', progress.error);
cb(fail && mode === 'verify' ? new Error('Sorry something was not quite right yet :(') : null);
if (progress.error) console.error("\nEncountered an error", progress.error);
cb(
fail && mode === "verify"
? new Error("Sorry something was not quite right yet :(")
: null
);
}

// give up after 3s, i.e. if server is irresponsive or doesn't quit
timeout = setTimeout(finish, 3000);

var client = new net.Socket();
log('Client connecting to server');
log("Client connecting to server");
client
.connect(PORT, HOST, function onconnection() {
progress.connected = true;
log('Client sending "%s"', msg);
client.write(msg);
})
.on('error', function onerror(err) {
})
.on("error", function onerror(err) {
progress.error = err;
finish();
})
.on('data', function ondata(data) {
progress.responded = true
.on("data", function ondata(data) {
progress.responded = true;
var res = data.toString();
log('Server responded with "%s"', msg);

progress.correctResponse = (res === msg)
progress.correctResponse = res === msg;

log('Telling server to quit');
client.write('QUIT');
log("Telling server to quit");
client.write("QUIT");
})
.on('close', function onclose() {
.on("close", function onclose() {
progress.closed = true;
log('Server closed connection')
finish()
})
log("Server closed connection");
finish();
});
}
6 changes: 3 additions & 3 deletions exercises/07_tcp_echo_server/problem.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ So lets start with the easy part.

### Starting the Server and Listening on a Port

First of all we need to initialize our server `tcp_server`.
First of all we need to initialize our server `tcp_server`.
This is done via [`uv_tcp_init`](http://docs.libuv.org/en/latest/tcp.html#c.uv_tcp_init).
Our server represents the TCP `handle`.

Expand Down Expand Up @@ -57,8 +57,8 @@ you to carefully read through the code and understand the concepts and technique

## Hint

Use `nc localhost 7000` to test your server (finish via `Ctrl-D`) and/or stop the server by sending `QUIT`.
You can also send entire files, i.e. `cat package.json | netcat localhost 7000`.
Use `nc localhost 7001` to test your server (finish via `Ctrl-D`) and/or stop the server by sending `QUIT`.
You can also send entire files, i.e. `cat package.json | netcat localhost 7001`.

On windows you may have to use telnet or putty.

Expand Down
Loading