aboutsummaryrefslogtreecommitdiff
path: root/docs/code
diff options
context:
space:
mode:
authorSaúl Ibarra Corretgé <saghul@gmail.com>2017-04-25 08:27:52 +0200
committerSaúl Ibarra Corretgé <saghul@gmail.com>2017-04-28 11:16:15 +0200
commitd59d6e6f22b4c11c37e34843d111a748df73bda2 (patch)
treef2a46fbfb3e308b3f474d086b122f4b591a78bcc /docs/code
parent2ce5734d76a8bfbf01af4a4854edf1e3cc42e029 (diff)
downloadlibuv-d59d6e6f22b4c11c37e34843d111a748df73bda2.tar.gz
libuv-d59d6e6f22b4c11c37e34843d111a748df73bda2.zip
doc: add code samples from uvbook (unadapted)
PR-URL: https://github.com/libuv/libuv/pull/1246 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
Diffstat (limited to 'docs/code')
-rw-r--r--docs/code/cgi/main.c81
-rw-r--r--docs/code/cgi/tick.c13
-rw-r--r--docs/code/detach/main.c31
-rw-r--r--docs/code/dns/main.c80
-rw-r--r--docs/code/helloworld/main.c15
-rw-r--r--docs/code/idle-basic/main.c24
-rw-r--r--docs/code/idle-compute/main.c43
-rw-r--r--docs/code/interfaces/main.c33
-rw-r--r--docs/code/locks/main.c57
-rw-r--r--docs/code/multi-echo-server/hammer.js20
-rw-r--r--docs/code/multi-echo-server/main.c114
-rw-r--r--docs/code/multi-echo-server/worker.c88
-rw-r--r--docs/code/onchange/main.c44
-rw-r--r--docs/code/pipe-echo-server/main.c88
-rw-r--r--docs/code/plugin/hello.c5
-rw-r--r--docs/code/plugin/main.c39
-rw-r--r--docs/code/plugin/plugin.h7
-rw-r--r--docs/code/proc-streams/main.c49
-rw-r--r--docs/code/proc-streams/test.c8
-rw-r--r--docs/code/progress/main.c47
-rw-r--r--docs/code/queue-cancel/main.c59
-rw-r--r--docs/code/queue-work/main.c44
-rw-r--r--docs/code/ref-timer/main.c29
-rw-r--r--docs/code/signal/main.c66
-rw-r--r--docs/code/spawn/main.c36
-rw-r--r--docs/code/tcp-echo-server/main.c83
-rw-r--r--docs/code/thread-create/main.c36
-rw-r--r--docs/code/tty-gravity/main.c48
-rw-r--r--docs/code/tty/main.c29
-rw-r--r--docs/code/udp-dhcp/main.c127
-rw-r--r--docs/code/uvcat/main.c63
-rw-r--r--docs/code/uvstop/main.c33
-rw-r--r--docs/code/uvtee/main.c80
-rw-r--r--docs/code/uvwget/main.c166
34 files changed, 1785 insertions, 0 deletions
diff --git a/docs/code/cgi/main.c b/docs/code/cgi/main.c
new file mode 100644
index 00000000..d2e34265
--- /dev/null
+++ b/docs/code/cgi/main.c
@@ -0,0 +1,81 @@
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uv.h>
+
+uv_loop_t *loop;
+uv_process_t child_req;
+uv_process_options_t options;
+
+void cleanup_handles(uv_process_t *req, int64_t exit_status, int term_signal) {
+ fprintf(stderr, "Process exited with status %" PRId64 ", signal %d\n", exit_status, term_signal);
+ uv_close((uv_handle_t*) req->data, NULL);
+ uv_close((uv_handle_t*) req, NULL);
+}
+
+void invoke_cgi_script(uv_tcp_t *client) {
+ size_t size = 500;
+ char path[size];
+ uv_exepath(path, &size);
+ strcpy(path + (strlen(path) - strlen("cgi")), "tick");
+
+ char* args[2];
+ args[0] = path;
+ args[1] = NULL;
+
+ /* ... finding the executable path and setting up arguments ... */
+
+ options.stdio_count = 3;
+ uv_stdio_container_t child_stdio[3];
+ child_stdio[0].flags = UV_IGNORE;
+ child_stdio[1].flags = UV_INHERIT_STREAM;
+ child_stdio[1].data.stream = (uv_stream_t*) client;
+ child_stdio[2].flags = UV_IGNORE;
+ options.stdio = child_stdio;
+
+ options.exit_cb = cleanup_handles;
+ options.file = args[0];
+ options.args = args;
+
+ // Set this so we can close the socket after the child process exits.
+ child_req.data = (void*) client;
+ int r;
+ if ((r = uv_spawn(loop, &child_req, &options))) {
+ fprintf(stderr, "%s\n", uv_strerror(r));
+ return;
+ }
+}
+
+void on_new_connection(uv_stream_t *server, int status) {
+ if (status == -1) {
+ // error!
+ return;
+ }
+
+ uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
+ uv_tcp_init(loop, client);
+ if (uv_accept(server, (uv_stream_t*) client) == 0) {
+ invoke_cgi_script(client);
+ }
+ else {
+ uv_close((uv_handle_t*) client, NULL);
+ }
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ uv_tcp_t server;
+ uv_tcp_init(loop, &server);
+
+ struct sockaddr_in bind_addr;
+ uv_ip4_addr("0.0.0.0", 7000, &bind_addr);
+ uv_tcp_bind(&server, (const struct sockaddr *)&bind_addr, 0);
+ int r = uv_listen((uv_stream_t*) &server, 128, on_new_connection);
+ if (r) {
+ fprintf(stderr, "Listen error %s\n", uv_err_name(r));
+ return 1;
+ }
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/cgi/tick.c b/docs/code/cgi/tick.c
new file mode 100644
index 00000000..0b498edf
--- /dev/null
+++ b/docs/code/cgi/tick.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <unistd.h>
+
+int main() {
+ int i;
+ for (i = 0; i < 10; i++) {
+ printf("tick\n");
+ fflush(stdout);
+ sleep(1);
+ }
+ printf("BOOM!\n");
+ return 0;
+}
diff --git a/docs/code/detach/main.c b/docs/code/detach/main.c
new file mode 100644
index 00000000..3c88fff4
--- /dev/null
+++ b/docs/code/detach/main.c
@@ -0,0 +1,31 @@
+#include <stdio.h>
+
+#include <uv.h>
+
+uv_loop_t *loop;
+uv_process_t child_req;
+uv_process_options_t options;
+
+int main() {
+ loop = uv_default_loop();
+
+ char* args[3];
+ args[0] = "sleep";
+ args[1] = "100";
+ args[2] = NULL;
+
+ options.exit_cb = NULL;
+ options.file = "sleep";
+ options.args = args;
+ options.flags = UV_PROCESS_DETACHED;
+
+ int r;
+ if ((r = uv_spawn(loop, &child_req, &options))) {
+ fprintf(stderr, "%s\n", uv_strerror(r));
+ return 1;
+ }
+ fprintf(stderr, "Launched sleep with PID %d\n", child_req.pid);
+ uv_unref((uv_handle_t*) &child_req);
+
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/dns/main.c b/docs/code/dns/main.c
new file mode 100644
index 00000000..77a7005f
--- /dev/null
+++ b/docs/code/dns/main.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uv.h>
+
+uv_loop_t *loop;
+
+void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
+ buf->base = malloc(suggested_size);
+ buf->len = suggested_size;
+}
+
+void on_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
+ if (nread < 0) {
+ if (nread != UV_EOF)
+ fprintf(stderr, "Read error %s\n", uv_err_name(nread));
+ uv_close((uv_handle_t*) client, NULL);
+ free(buf->base);
+ free(client);
+ return;
+ }
+
+ char *data = (char*) malloc(sizeof(char) * (nread+1));
+ data[nread] = '\0';
+ strncpy(data, buf->base, nread);
+
+ fprintf(stderr, "%s", data);
+ free(data);
+ free(buf->base);
+}
+
+void on_connect(uv_connect_t *req, int status) {
+ if (status < 0) {
+ fprintf(stderr, "connect failed error %s\n", uv_err_name(status));
+ free(req);
+ return;
+ }
+
+ uv_read_start((uv_stream_t*) req->handle, alloc_buffer, on_read);
+ free(req);
+}
+
+void on_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) {
+ if (status < 0) {
+ fprintf(stderr, "getaddrinfo callback error %s\n", uv_err_name(status));
+ return;
+ }
+
+ char addr[17] = {'\0'};
+ uv_ip4_name((struct sockaddr_in*) res->ai_addr, addr, 16);
+ fprintf(stderr, "%s\n", addr);
+
+ uv_connect_t *connect_req = (uv_connect_t*) malloc(sizeof(uv_connect_t));
+ uv_tcp_t *socket = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
+ uv_tcp_init(loop, socket);
+
+ uv_tcp_connect(connect_req, socket, (const struct sockaddr*) res->ai_addr, on_connect);
+
+ uv_freeaddrinfo(res);
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ struct addrinfo hints;
+ hints.ai_family = PF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_flags = 0;
+
+ uv_getaddrinfo_t resolver;
+ fprintf(stderr, "irc.freenode.net is... ");
+ int r = uv_getaddrinfo(loop, &resolver, on_resolved, "irc.freenode.net", "6667", &hints);
+
+ if (r) {
+ fprintf(stderr, "getaddrinfo call error %s\n", uv_err_name(r));
+ return 1;
+ }
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/helloworld/main.c b/docs/code/helloworld/main.c
new file mode 100644
index 00000000..a31bf88a
--- /dev/null
+++ b/docs/code/helloworld/main.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <uv.h>
+
+int main() {
+ uv_loop_t *loop = malloc(sizeof(uv_loop_t));
+ uv_loop_init(loop);
+
+ printf("Now quitting.\n");
+ uv_run(loop, UV_RUN_DEFAULT);
+
+ uv_loop_close(loop);
+ free(loop);
+ return 0;
+}
diff --git a/docs/code/idle-basic/main.c b/docs/code/idle-basic/main.c
new file mode 100644
index 00000000..77ba31cf
--- /dev/null
+++ b/docs/code/idle-basic/main.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <uv.h>
+
+int64_t counter = 0;
+
+void wait_for_a_while(uv_idle_t* handle) {
+ counter++;
+
+ if (counter >= 10e6)
+ uv_idle_stop(handle);
+}
+
+int main() {
+ uv_idle_t idler;
+
+ uv_idle_init(uv_default_loop(), &idler);
+ uv_idle_start(&idler, wait_for_a_while);
+
+ printf("Idling...\n");
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+ uv_loop_close(uv_default_loop());
+ return 0;
+}
diff --git a/docs/code/idle-compute/main.c b/docs/code/idle-compute/main.c
new file mode 100644
index 00000000..ff44b694
--- /dev/null
+++ b/docs/code/idle-compute/main.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+
+#include <uv.h>
+
+uv_loop_t *loop;
+uv_fs_t stdin_watcher;
+uv_idle_t idler;
+char buffer[1024];
+
+void crunch_away(uv_idle_t* handle) {
+ // Compute extra-terrestrial life
+ // fold proteins
+ // computer another digit of PI
+ // or similar
+ fprintf(stderr, "Computing PI...\n");
+ // just to avoid overwhelming your terminal emulator
+ uv_idle_stop(handle);
+}
+
+void on_type(uv_fs_t *req) {
+ if (stdin_watcher.result > 0) {
+ buffer[stdin_watcher.result] = '\0';
+ printf("Typed %s\n", buffer);
+
+ uv_buf_t buf = uv_buf_init(buffer, 1024);
+ uv_fs_read(loop, &stdin_watcher, 0, &buf, 1, -1, on_type);
+ uv_idle_start(&idler, crunch_away);
+ }
+ else if (stdin_watcher.result < 0) {
+ fprintf(stderr, "error opening file: %s\n", uv_strerror(req->result));
+ }
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ uv_idle_init(loop, &idler);
+
+ uv_buf_t buf = uv_buf_init(buffer, 1024);
+ uv_fs_read(loop, &stdin_watcher, 0, &buf, 1, -1, on_type);
+ uv_idle_start(&idler, crunch_away);
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/interfaces/main.c b/docs/code/interfaces/main.c
new file mode 100644
index 00000000..cac12c26
--- /dev/null
+++ b/docs/code/interfaces/main.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <uv.h>
+
+int main() {
+ char buf[512];
+ uv_interface_address_t *info;
+ int count, i;
+
+ uv_interface_addresses(&info, &count);
+ i = count;
+
+ printf("Number of interfaces: %d\n", count);
+ while (i--) {
+ uv_interface_address_t interface = info[i];
+
+ printf("Name: %s\n", interface.name);
+ printf("Internal? %s\n", interface.is_internal ? "Yes" : "No");
+
+ if (interface.address.address4.sin_family == AF_INET) {
+ uv_ip4_name(&interface.address.address4, buf, sizeof(buf));
+ printf("IPv4 address: %s\n", buf);
+ }
+ else if (interface.address.address4.sin_family == AF_INET6) {
+ uv_ip6_name(&interface.address.address6, buf, sizeof(buf));
+ printf("IPv6 address: %s\n", buf);
+ }
+
+ printf("\n");
+ }
+
+ uv_free_interface_addresses(info, count);
+ return 0;
+}
diff --git a/docs/code/locks/main.c b/docs/code/locks/main.c
new file mode 100644
index 00000000..2b1f8ca7
--- /dev/null
+++ b/docs/code/locks/main.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <uv.h>
+
+uv_barrier_t blocker;
+uv_rwlock_t numlock;
+int shared_num;
+
+void reader(void *n)
+{
+ int num = *(int *)n;
+ int i;
+ for (i = 0; i < 20; i++) {
+ uv_rwlock_rdlock(&numlock);
+ printf("Reader %d: acquired lock\n", num);
+ printf("Reader %d: shared num = %d\n", num, shared_num);
+ uv_rwlock_rdunlock(&numlock);
+ printf("Reader %d: released lock\n", num);
+ }
+ uv_barrier_wait(&blocker);
+}
+
+void writer(void *n)
+{
+ int num = *(int *)n;
+ int i;
+ for (i = 0; i < 20; i++) {
+ uv_rwlock_wrlock(&numlock);
+ printf("Writer %d: acquired lock\n", num);
+ shared_num++;
+ printf("Writer %d: incremented shared num = %d\n", num, shared_num);
+ uv_rwlock_wrunlock(&numlock);
+ printf("Writer %d: released lock\n", num);
+ }
+ uv_barrier_wait(&blocker);
+}
+
+int main()
+{
+ uv_barrier_init(&blocker, 4);
+
+ shared_num = 0;
+ uv_rwlock_init(&numlock);
+
+ uv_thread_t threads[3];
+
+ int thread_nums[] = {1, 2, 1};
+ uv_thread_create(&threads[0], reader, &thread_nums[0]);
+ uv_thread_create(&threads[1], reader, &thread_nums[1]);
+
+ uv_thread_create(&threads[2], writer, &thread_nums[2]);
+
+ uv_barrier_wait(&blocker);
+ uv_barrier_destroy(&blocker);
+
+ uv_rwlock_destroy(&numlock);
+ return 0;
+}
diff --git a/docs/code/multi-echo-server/hammer.js b/docs/code/multi-echo-server/hammer.js
new file mode 100644
index 00000000..5df345b7
--- /dev/null
+++ b/docs/code/multi-echo-server/hammer.js
@@ -0,0 +1,20 @@
+var net = require('net');
+
+var PHRASE = "hello world";
+var write = function(socket) {
+ socket.write(PHRASE, 'utf8');
+}
+
+for (var i = 0; i < 1000; i++) {
+(function() {
+ var socket = net.connect(7000, 'localhost', function() {
+ socket.on('data', function(reply) {
+ if (reply.toString().indexOf(PHRASE) != 0)
+ console.error("Problem! '" + reply + "'" + " '" + PHRASE + "'");
+ else
+ write(socket);
+ });
+ write(socket);
+ });
+})();
+}
diff --git a/docs/code/multi-echo-server/main.c b/docs/code/multi-echo-server/main.c
new file mode 100644
index 00000000..25f49612
--- /dev/null
+++ b/docs/code/multi-echo-server/main.c
@@ -0,0 +1,114 @@
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uv.h>
+
+uv_loop_t *loop;
+
+struct child_worker {
+ uv_process_t req;
+ uv_process_options_t options;
+ uv_pipe_t pipe;
+} *workers;
+
+int round_robin_counter;
+int child_worker_count;
+
+uv_buf_t dummy_buf;
+char worker_path[500];
+
+void close_process_handle(uv_process_t *req, int64_t exit_status, int term_signal) {
+ fprintf(stderr, "Process exited with status %" PRId64 ", signal %d\n", exit_status, term_signal);
+ uv_close((uv_handle_t*) req, NULL);
+}
+
+void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
+ buf->base = malloc(suggested_size);
+ buf->len = suggested_size;
+}
+
+void on_new_connection(uv_stream_t *server, int status) {
+ if (status == -1) {
+ // error!
+ return;
+ }
+
+ uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
+ uv_tcp_init(loop, client);
+ if (uv_accept(server, (uv_stream_t*) client) == 0) {
+ uv_write_t *write_req = (uv_write_t*) malloc(sizeof(uv_write_t));
+ dummy_buf = uv_buf_init("a", 1);
+ struct child_worker *worker = &workers[round_robin_counter];
+ uv_write2(write_req, (uv_stream_t*) &worker->pipe, &dummy_buf, 1, (uv_stream_t*) client, NULL);
+ round_robin_counter = (round_robin_counter + 1) % child_worker_count;
+ }
+ else {
+ uv_close((uv_handle_t*) client, NULL);
+ }
+}
+
+void setup_workers() {
+ size_t path_size = 500;
+ uv_exepath(worker_path, &path_size);
+ strcpy(worker_path + (strlen(worker_path) - strlen("multi-echo-server")), "worker");
+ fprintf(stderr, "Worker path: %s\n", worker_path);
+
+ char* args[2];
+ args[0] = worker_path;
+ args[1] = NULL;
+
+ round_robin_counter = 0;
+
+ // ...
+
+ // launch same number of workers as number of CPUs
+ uv_cpu_info_t *info;
+ int cpu_count;
+ uv_cpu_info(&info, &cpu_count);
+ uv_free_cpu_info(info, cpu_count);
+
+ child_worker_count = cpu_count;
+
+ workers = calloc(sizeof(struct child_worker), cpu_count);
+ while (cpu_count--) {
+ struct child_worker *worker = &workers[cpu_count];
+ uv_pipe_init(loop, &worker->pipe, 1);
+
+ uv_stdio_container_t child_stdio[3];
+ child_stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
+ child_stdio[0].data.stream = (uv_stream_t*) &worker->pipe;
+ child_stdio[1].flags = UV_IGNORE;
+ child_stdio[2].flags = UV_INHERIT_FD;
+ child_stdio[2].data.fd = 2;
+
+ worker->options.stdio = child_stdio;
+ worker->options.stdio_count = 3;
+
+ worker->options.exit_cb = close_process_handle;
+ worker->options.file = args[0];
+ worker->options.args = args;
+
+ uv_spawn(loop, &worker->req, &worker->options);
+ fprintf(stderr, "Started worker %d\n", worker->req.pid);
+ }
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ setup_workers();
+
+ uv_tcp_t server;
+ uv_tcp_init(loop, &server);
+
+ struct sockaddr_in bind_addr;
+ uv_ip4_addr("0.0.0.0", 7000, &bind_addr);
+ uv_tcp_bind(&server, (const struct sockaddr *)&bind_addr, 0);
+ int r;
+ if ((r = uv_listen((uv_stream_t*) &server, 128, on_new_connection))) {
+ fprintf(stderr, "Listen error %s\n", uv_err_name(r));
+ return 2;
+ }
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/multi-echo-server/worker.c b/docs/code/multi-echo-server/worker.c
new file mode 100644
index 00000000..1c465759
--- /dev/null
+++ b/docs/code/multi-echo-server/worker.c
@@ -0,0 +1,88 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <uv.h>
+
+uv_loop_t *loop;
+uv_pipe_t queue;
+
+typedef struct {
+ uv_write_t req;
+ uv_buf_t buf;
+} write_req_t;
+
+void free_write_req(uv_write_t *req) {
+ write_req_t *wr = (write_req_t*) req;
+ free(wr->buf.base);
+ free(wr);
+}
+
+void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
+ buf->base = malloc(suggested_size);
+ buf->len = suggested_size;
+}
+
+void echo_write(uv_write_t *req, int status) {
+ if (status) {
+ fprintf(stderr, "Write error %s\n", uv_err_name(status));
+ }
+ free_write_req(req);
+}
+
+void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
+ if (nread > 0) {
+ write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t));
+ req->buf = uv_buf_init(buf->base, nread);
+ uv_write((uv_write_t*) req, client, &req->buf, 1, echo_write);
+ return;
+ }
+
+ if (nread < 0) {
+ if (nread != UV_EOF)
+ fprintf(stderr, "Read error %s\n", uv_err_name(nread));
+ uv_close((uv_handle_t*) client, NULL);
+ }
+
+ free(buf->base);
+}
+
+void on_new_connection(uv_stream_t *q, ssize_t nread, const uv_buf_t *buf) {
+ if (nread < 0) {
+ if (nread != UV_EOF)
+ fprintf(stderr, "Read error %s\n", uv_err_name(nread));
+ uv_close((uv_handle_t*) q, NULL);
+ return;
+ }
+
+ uv_pipe_t *pipe = (uv_pipe_t*) q;
+ if (!uv_pipe_pending_count(pipe)) {
+ fprintf(stderr, "No pending count\n");
+ return;
+ }
+
+ uv_handle_type pending = uv_pipe_pending_type(pipe);
+ assert(pending == UV_TCP);
+
+ uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
+ uv_tcp_init(loop, client);
+ if (uv_accept(q, (uv_stream_t*) client) == 0) {
+ uv_os_fd_t fd;
+ uv_fileno((const uv_handle_t*) client, &fd);
+ fprintf(stderr, "Worker %d: Accepted fd %d\n", getpid(), fd);
+ uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);
+ }
+ else {
+ uv_close((uv_handle_t*) client, NULL);
+ }
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ uv_pipe_init(loop, &queue, 1 /* ipc */);
+ uv_pipe_open(&queue, 0);
+ uv_read_start((uv_stream_t*)&queue, alloc_buffer, on_new_connection);
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/onchange/main.c b/docs/code/onchange/main.c
new file mode 100644
index 00000000..40bdaa52
--- /dev/null
+++ b/docs/code/onchange/main.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <uv.h>
+
+uv_loop_t *loop;
+const char *command;
+
+void run_command(uv_fs_event_t *handle, const char *filename, int events, int status) {
+ char path[1024];
+ size_t size = 1023;
+ // Does not handle error if path is longer than 1023.
+ uv_fs_event_getpath(handle, path, &size);
+ path[size] = '\0';
+
+ fprintf(stderr, "Change detected in %s: ", path);
+ if (events & UV_RENAME)
+ fprintf(stderr, "renamed");
+ if (events & UV_CHANGE)
+ fprintf(stderr, "changed");
+
+ fprintf(stderr, " %s\n", filename ? filename : "");
+ system(command);
+}
+
+int main(int argc, char **argv) {
+ if (argc <= 2) {
+ fprintf(stderr, "Usage: %s <command> <file1> [file2 ...]\n", argv[0]);
+ return 1;
+ }
+
+ loop = uv_default_loop();
+ command = argv[1];
+
+ while (argc-- > 2) {
+ fprintf(stderr, "Adding watch on %s\n", argv[argc]);
+ uv_fs_event_t *fs_event_req = malloc(sizeof(uv_fs_event_t));
+ uv_fs_event_init(loop, fs_event_req);
+ // The recursive flag watches subdirectories too.
+ uv_fs_event_start(fs_event_req, run_command, argv[argc], UV_FS_EVENT_RECURSIVE);
+ }
+
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/pipe-echo-server/main.c b/docs/code/pipe-echo-server/main.c
new file mode 100644
index 00000000..4ba4246e
--- /dev/null
+++ b/docs/code/pipe-echo-server/main.c
@@ -0,0 +1,88 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uv.h>
+
+uv_loop_t *loop;
+
+typedef struct {
+ uv_write_t req;
+ uv_buf_t buf;
+} write_req_t;
+
+void free_write_req(uv_write_t *req) {
+ write_req_t *wr = (write_req_t*) req;
+ free(wr->buf.base);
+ free(wr);
+}
+
+void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
+ buf->base = malloc(suggested_size);
+ buf->len = suggested_size;
+}
+
+void echo_write(uv_write_t *req, int status) {
+ if (status < 0) {
+ fprintf(stderr, "Write error %s\n", uv_err_name(status));
+ }
+ free_write_req(req);
+}
+
+void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
+ if (nread > 0) {
+ write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t));
+ req->buf = uv_buf_init(buf->base, nread);
+ uv_write((uv_write_t*) req, client, &req->buf, 1, echo_write);
+ return;
+ }
+
+ if (nread < 0) {
+ if (nread != UV_EOF)
+ fprintf(stderr, "Read error %s\n", uv_err_name(nread));
+ uv_close((uv_handle_t*) client, NULL);
+ }
+
+ free(buf->base);
+}
+
+void on_new_connection(uv_stream_t *server, int status) {
+ if (status == -1) {
+ // error!
+ return;
+ }
+
+ uv_pipe_t *client = (uv_pipe_t*) malloc(sizeof(uv_pipe_t));
+ uv_pipe_init(loop, client, 0);
+ if (uv_accept(server, (uv_stream_t*) client) == 0) {
+ uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);
+ }
+ else {
+ uv_close((uv_handle_t*) client, NULL);
+ }
+}
+
+void remove_sock(int sig) {
+ uv_fs_t req;
+ uv_fs_unlink(loop, &req, "echo.sock", NULL);
+ exit(0);
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ uv_pipe_t server;
+ uv_pipe_init(loop, &server, 0);
+
+ signal(SIGINT, remove_sock);
+
+ int r;
+ if ((r = uv_pipe_bind(&server, "echo.sock"))) {
+ fprintf(stderr, "Bind error %s\n", uv_err_name(r));
+ return 1;
+ }
+ if ((r = uv_listen((uv_stream_t*) &server, 128, on_new_connection))) {
+ fprintf(stderr, "Listen error %s\n", uv_err_name(r));
+ return 2;
+ }
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/plugin/hello.c b/docs/code/plugin/hello.c
new file mode 100644
index 00000000..7b2861d7
--- /dev/null
+++ b/docs/code/plugin/hello.c
@@ -0,0 +1,5 @@
+#include "plugin.h"
+
+void initialize() {
+ mfp_register("Hello World!");
+}
diff --git a/docs/code/plugin/main.c b/docs/code/plugin/main.c
new file mode 100644
index 00000000..06e581e6
--- /dev/null
+++ b/docs/code/plugin/main.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <uv.h>
+
+#include "plugin.h"
+
+typedef void (*init_plugin_function)();
+
+void mfp_register(const char *name) {
+ fprintf(stderr, "Registered plugin \"%s\"\n", name);
+}
+
+int main(int argc, char **argv) {
+ if (argc == 1) {
+ fprintf(stderr, "Usage: %s [plugin1] [plugin2] ...\n", argv[0]);
+ return 0;
+ }
+
+ uv_lib_t *lib = (uv_lib_t*) malloc(sizeof(uv_lib_t));
+ while (--argc) {
+ fprintf(stderr, "Loading %s\n", argv[argc]);
+ if (uv_dlopen(argv[argc], lib)) {
+ fprintf(stderr, "Error: %s\n", uv_dlerror(lib));
+ continue;
+ }
+
+ init_plugin_function init_plugin;
+ if (uv_dlsym(lib, "initialize", (void **) &init_plugin)) {
+ fprintf(stderr, "dlsym error: %s\n", uv_dlerror(lib));
+ continue;
+ }
+
+ init_plugin();
+ }
+
+ return 0;
+}
diff --git a/docs/code/plugin/plugin.h b/docs/code/plugin/plugin.h
new file mode 100644
index 00000000..21f194e6
--- /dev/null
+++ b/docs/code/plugin/plugin.h
@@ -0,0 +1,7 @@
+#ifndef UVBOOK_PLUGIN_SYSTEM
+#define UVBOOK_PLUGIN_SYSTEM
+
+// Plugin authors should use this to register their plugins with mfp.
+void mfp_register(const char *name);
+
+#endif
diff --git a/docs/code/proc-streams/main.c b/docs/code/proc-streams/main.c
new file mode 100644
index 00000000..b8a65212
--- /dev/null
+++ b/docs/code/proc-streams/main.c
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <uv.h>
+
+uv_loop_t *loop;
+uv_process_t child_req;
+uv_process_options_t options;
+
+void on_exit(uv_process_t *req, int64_t exit_status, int term_signal) {
+ fprintf(stderr, "Process exited with status %" PRId64 ", signal %d\n", exit_status, term_signal);
+ uv_close((uv_handle_t*) req, NULL);
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ size_t size = 500;
+ char path[size];
+ uv_exepath(path, &size);
+ strcpy(path + (strlen(path) - strlen("proc-streams")), "test");
+
+ char* args[2];
+ args[0] = path;
+ args[1] = NULL;
+
+ /* ... */
+
+ options.stdio_count = 3;
+ uv_stdio_container_t child_stdio[3];
+ child_stdio[0].flags = UV_IGNORE;
+ child_stdio[1].flags = UV_IGNORE;
+ child_stdio[2].flags = UV_INHERIT_FD;
+ child_stdio[2].data.fd = 2;
+ options.stdio = child_stdio;
+
+ options.exit_cb = on_exit;
+ options.file = args[0];
+ options.args = args;
+
+ int r;
+ if ((r = uv_spawn(loop, &child_req, &options))) {
+ fprintf(stderr, "%s\n", uv_strerror(r));
+ return 1;
+ }
+
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/proc-streams/test.c b/docs/code/proc-streams/test.c
new file mode 100644
index 00000000..7c45c1fd
--- /dev/null
+++ b/docs/code/proc-streams/test.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int main()
+{
+ fprintf(stderr, "This is stderr\n");
+ printf("This is stdout\n");
+ return 0;
+}
diff --git a/docs/code/progress/main.c b/docs/code/progress/main.c
new file mode 100644
index 00000000..5af01f14
--- /dev/null
+++ b/docs/code/progress/main.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <uv.h>
+
+uv_loop_t *loop;
+uv_async_t async;
+
+double percentage;
+
+void fake_download(uv_work_t *req) {
+ int size = *((int*) req->data);
+ int downloaded = 0;
+ while (downloaded < size) {
+ percentage = downloaded*100.0/size;
+ async.data = (void*) &percentage;
+ uv_async_send(&async);
+
+ sleep(1);
+ downloaded += (200+random())%1000; // can only download max 1000bytes/sec,
+ // but at least a 200;
+ }
+}
+
+void after(uv_work_t *req, int status) {
+ fprintf(stderr, "Download complete\n");
+ uv_close((uv_handle_t*) &async, NULL);
+}
+
+void print_progress(uv_async_t *handle) {
+ double percentage = *((double*) handle->data);
+ fprintf(stderr, "Downloaded %.2f%%\n", percentage);
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ uv_work_t req;
+ int size = 10240;
+ req.data = (void*) &size;
+
+ uv_async_init(loop, &async, print_progress);
+ uv_queue_work(loop, &req, fake_download, after);
+
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/queue-cancel/main.c b/docs/code/queue-cancel/main.c
new file mode 100644
index 00000000..3f7836cb
--- /dev/null
+++ b/docs/code/queue-cancel/main.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <uv.h>
+
+#define FIB_UNTIL 25
+uv_loop_t *loop;
+uv_work_t fib_reqs[FIB_UNTIL];
+
+long fib_(long t) {
+ if (t == 0 || t == 1)
+ return 1;
+ else
+ return fib_(t-1) + fib_(t-2);
+}
+
+void fib(uv_work_t *req) {
+ int n = *(int *) req->data;
+ if (random() % 2)
+ sleep(1);
+ else
+ sleep(3);
+ long fib = fib_(n);
+ fprintf(stderr, "%dth fibonacci is %lu\n", n, fib);
+}
+
+void after_fib(uv_work_t *req, int status) {
+ if (status == UV_ECANCELED)
+ fprintf(stderr, "Calculation of %d cancelled.\n", *(int *) req->data);
+}
+
+void signal_handler(uv_signal_t *req, int signum)
+{
+ printf("Signal received!\n");
+ int i;
+ for (i = 0; i < FIB_UNTIL; i++) {
+ uv_cancel((uv_req_t*) &fib_reqs[i]);
+ }
+ uv_signal_stop(req);
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ int data[FIB_UNTIL];
+ int i;
+ for (i = 0; i < FIB_UNTIL; i++) {
+ data[i] = i;
+ fib_reqs[i].data = (void *) &data[i];
+ uv_queue_work(loop, &fib_reqs[i], fib, after_fib);
+ }
+
+ uv_signal_t sig;
+ uv_signal_init(loop, &sig);
+ uv_signal_start(&sig, signal_handler, SIGINT);
+
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/queue-work/main.c b/docs/code/queue-work/main.c
new file mode 100644
index 00000000..55675ea0
--- /dev/null
+++ b/docs/code/queue-work/main.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <uv.h>
+
+#define FIB_UNTIL 25
+uv_loop_t *loop;
+
+long fib_(long t) {
+ if (t == 0 || t == 1)
+ return 1;
+ else
+ return fib_(t-1) + fib_(t-2);
+}
+
+void fib(uv_work_t *req) {
+ int n = *(int *) req->data;
+ if (random() % 2)
+ sleep(1);
+ else
+ sleep(3);
+ long fib = fib_(n);
+ fprintf(stderr, "%dth fibonacci is %lu\n", n, fib);
+}
+
+void after_fib(uv_work_t *req, int status) {
+ fprintf(stderr, "Done calculating %dth fibonacci\n", *(int *) req->data);
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ int data[FIB_UNTIL];
+ uv_work_t req[FIB_UNTIL];
+ int i;
+ for (i = 0; i < FIB_UNTIL; i++) {
+ data[i] = i;
+ req[i].data = (void *) &data[i];
+ uv_queue_work(loop, &req[i], fib, after_fib);
+ }
+
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/ref-timer/main.c b/docs/code/ref-timer/main.c
new file mode 100644
index 00000000..ad7c8295
--- /dev/null
+++ b/docs/code/ref-timer/main.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+
+#include <uv.h>
+
+uv_loop_t *loop;
+uv_timer_t gc_req;
+uv_timer_t fake_job_req;
+
+void gc(uv_timer_t *handle) {
+ fprintf(stderr, "Freeing unused objects\n");
+}
+
+void fake_job(uv_timer_t *handle) {
+ fprintf(stdout, "Fake job done\n");
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ uv_timer_init(loop, &gc_req);
+ uv_unref((uv_handle_t*) &gc_req);
+
+ uv_timer_start(&gc_req, gc, 0, 2000);
+
+ // could actually be a TCP download or something
+ uv_timer_init(loop, &fake_job_req);
+ uv_timer_start(&fake_job_req, fake_job, 9000, 0);
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/signal/main.c b/docs/code/signal/main.c
new file mode 100644
index 00000000..1b982c5a
--- /dev/null
+++ b/docs/code/signal/main.c
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <uv.h>
+
+uv_loop_t* create_loop()
+{
+ uv_loop_t *loop = malloc(sizeof(uv_loop_t));
+ if (loop) {
+ uv_loop_init(loop);
+ }
+ return loop;
+}
+
+void signal_handler(uv_signal_t *handle, int signum)
+{
+ printf("Signal received: %d\n", signum);
+ uv_signal_stop(handle);
+}
+
+// two signal handlers in one loop
+void thread1_worker(void *userp)
+{
+ uv_loop_t *loop1 = create_loop();
+
+ uv_signal_t sig1a, sig1b;
+ uv_signal_init(loop1, &sig1a);
+ uv_signal_start(&sig1a, signal_handler, SIGUSR1);
+
+ uv_signal_init(loop1, &sig1b);
+ uv_signal_start(&sig1b, signal_handler, SIGUSR1);
+
+ uv_run(loop1, UV_RUN_DEFAULT);
+}
+
+// two signal handlers, each in its own loop
+void thread2_worker(void *userp)
+{
+ uv_loop_t *loop2 = create_loop();
+ uv_loop_t *loop3 = create_loop();
+
+ uv_signal_t sig2;
+ uv_signal_init(loop2, &sig2);
+ uv_signal_start(&sig2, signal_handler, SIGUSR1);
+
+ uv_signal_t sig3;
+ uv_signal_init(loop3, &sig3);
+ uv_signal_start(&sig3, signal_handler, SIGUSR1);
+
+ while (uv_run(loop2, UV_RUN_NOWAIT) || uv_run(loop3, UV_RUN_NOWAIT)) {
+ }
+}
+
+int main()
+{
+ printf("PID %d\n", getpid());
+
+ uv_thread_t thread1, thread2;
+
+ uv_thread_create(&thread1, thread1_worker, 0);
+ uv_thread_create(&thread2, thread2_worker, 0);
+
+ uv_thread_join(&thread1);
+ uv_thread_join(&thread2);
+ return 0;
+}
diff --git a/docs/code/spawn/main.c b/docs/code/spawn/main.c
new file mode 100644
index 00000000..dedfe00c
--- /dev/null
+++ b/docs/code/spawn/main.c
@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include <inttypes.h>
+
+#include <uv.h>
+
+uv_loop_t *loop;
+uv_process_t child_req;
+uv_process_options_t options;
+
+void on_exit(uv_process_t *req, int64_t exit_status, int term_signal) {
+ fprintf(stderr, "Process exited with status %" PRId64 ", signal %d\n", exit_status, term_signal);
+ uv_close((uv_handle_t*) req, NULL);
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ char* args[3];
+ args[0] = "mkdir";
+ args[1] = "test-dir";
+ args[2] = NULL;
+
+ options.exit_cb = on_exit;
+ options.file = "mkdir";
+ options.args = args;
+
+ int r;
+ if ((r = uv_spawn(loop, &child_req, &options))) {
+ fprintf(stderr, "%s\n", uv_strerror(r));
+ return 1;
+ } else {
+ fprintf(stderr, "Launched process with ID %d\n", child_req.pid);
+ }
+
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/tcp-echo-server/main.c b/docs/code/tcp-echo-server/main.c
new file mode 100644
index 00000000..63965bd9
--- /dev/null
+++ b/docs/code/tcp-echo-server/main.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uv.h>
+
+#define DEFAULT_PORT 7000
+#define DEFAULT_BACKLOG 128
+
+uv_loop_t *loop;
+struct sockaddr_in addr;
+
+typedef struct {
+ uv_write_t req;
+ uv_buf_t buf;
+} write_req_t;
+
+void free_write_req(uv_write_t *req) {
+ write_req_t *wr = (write_req_t*) req;
+ free(wr->buf.base);
+ free(wr);
+}
+
+void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
+ buf->base = (char*) malloc(suggested_size);
+ buf->len = suggested_size;
+}
+
+void echo_write(uv_write_t *req, int status) {
+ if (status) {
+ fprintf(stderr, "Write error %s\n", uv_strerror(status));
+ }
+ free_write_req(req);
+}
+
+void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
+ if (nread > 0) {
+ write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t));
+ req->buf = uv_buf_init(buf->base, nread);
+ uv_write((uv_write_t*) req, client, &req->buf, 1, echo_write);
+ return;
+ }
+ if (nread < 0) {
+ if (nread != UV_EOF)
+ fprintf(stderr, "Read error %s\n", uv_err_name(nread));
+ uv_close((uv_handle_t*) client, NULL);
+ }
+
+ free(buf->base);
+}
+
+void on_new_connection(uv_stream_t *server, int status) {
+ if (status < 0) {
+ fprintf(stderr, "New connection error %s\n", uv_strerror(status));
+ // error!
+ return;
+ }
+
+ uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
+ uv_tcp_init(loop, client);
+ if (uv_accept(server, (uv_stream_t*) client) == 0) {
+ uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);
+ }
+ else {
+ uv_close((uv_handle_t*) client, NULL);
+ }
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ uv_tcp_t server;
+ uv_tcp_init(loop, &server);
+
+ uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr);
+
+ uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
+ int r = uv_listen((uv_stream_t*) &server, DEFAULT_BACKLOG, on_new_connection);
+ if (r) {
+ fprintf(stderr, "Listen error %s\n", uv_strerror(r));
+ return 1;
+ }
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/thread-create/main.c b/docs/code/thread-create/main.c
new file mode 100644
index 00000000..70224c1e
--- /dev/null
+++ b/docs/code/thread-create/main.c
@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include <unistd.h>
+
+#include <uv.h>
+
+void hare(void *arg) {
+ int tracklen = *((int *) arg);
+ while (tracklen) {
+ tracklen--;
+ sleep(1);
+ fprintf(stderr, "Hare ran another step\n");
+ }
+ fprintf(stderr, "Hare done running!\n");
+}
+
+void tortoise(void *arg) {
+ int tracklen = *((int *) arg);
+ while (tracklen) {
+ tracklen--;
+ fprintf(stderr, "Tortoise ran another step\n");
+ sleep(3);
+ }
+ fprintf(stderr, "Tortoise done running!\n");
+}
+
+int main() {
+ int tracklen = 10;
+ uv_thread_t hare_id;
+ uv_thread_t tortoise_id;
+ uv_thread_create(&hare_id, hare, &tracklen);
+ uv_thread_create(&tortoise_id, tortoise, &tracklen);
+
+ uv_thread_join(&hare_id);
+ uv_thread_join(&tortoise_id);
+ return 0;
+}
diff --git a/docs/code/tty-gravity/main.c b/docs/code/tty-gravity/main.c
new file mode 100644
index 00000000..053e7e59
--- /dev/null
+++ b/docs/code/tty-gravity/main.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <uv.h>
+
+uv_loop_t *loop;
+uv_tty_t tty;
+uv_timer_t tick;
+uv_write_t write_req;
+int width, height;
+int pos = 0;
+char *message = " Hello TTY ";
+
+void update(uv_timer_t *req) {
+ char data[500];
+
+ uv_buf_t buf;
+ buf.base = data;
+ buf.len = sprintf(data, "\033[2J\033[H\033[%dB\033[%luC\033[42;37m%s",
+ pos,
+ (unsigned long) (width-strlen(message))/2,
+ message);
+ uv_write(&write_req, (uv_stream_t*) &tty, &buf, 1, NULL);
+
+ pos++;
+ if (pos > height) {
+ uv_tty_reset_mode();
+ uv_timer_stop(&tick);
+ }
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ uv_tty_init(loop, &tty, 1, 0);
+ uv_tty_set_mode(&tty, 0);
+
+ if (uv_tty_get_winsize(&tty, &width, &height)) {
+ fprintf(stderr, "Could not get TTY information\n");
+ uv_tty_reset_mode();
+ return 1;
+ }
+
+ fprintf(stderr, "Width %d, height %d\n", width, height);
+ uv_timer_init(loop, &tick);
+ uv_timer_start(&tick, update, 200, 200);
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/tty/main.c b/docs/code/tty/main.c
new file mode 100644
index 00000000..03b26fbc
--- /dev/null
+++ b/docs/code/tty/main.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <uv.h>
+
+uv_loop_t *loop;
+uv_tty_t tty;
+int main() {
+ loop = uv_default_loop();
+
+ uv_tty_init(loop, &tty, 1, 0);
+ uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL);
+
+ if (uv_guess_handle(1) == UV_TTY) {
+ uv_write_t req;
+ uv_buf_t buf;
+ buf.base = "\033[41;37m";
+ buf.len = strlen(buf.base);
+ uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL);
+ }
+
+ uv_write_t req;
+ uv_buf_t buf;
+ buf.base = "Hello TTY\n";
+ buf.len = strlen(buf.base);
+ uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL);
+ uv_tty_reset_mode();
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/udp-dhcp/main.c b/docs/code/udp-dhcp/main.c
new file mode 100644
index 00000000..fc2ca0c8
--- /dev/null
+++ b/docs/code/udp-dhcp/main.c
@@ -0,0 +1,127 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <uv.h>
+
+uv_loop_t *loop;
+uv_udp_t send_socket;
+uv_udp_t recv_socket;
+
+void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
+ buf->base = malloc(suggested_size);
+ buf->len = suggested_size;
+}
+
+void on_read(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) {
+ if (nread < 0) {
+ fprintf(stderr, "Read error %s\n", uv_err_name(nread));
+ uv_close((uv_handle_t*) req, NULL);
+ free(buf->base);
+ return;
+ }
+
+ char sender[17] = { 0 };
+ uv_ip4_name((const struct sockaddr_in*) addr, sender, 16);
+ fprintf(stderr, "Recv from %s\n", sender);
+
+ // ... DHCP specific code
+ unsigned int *as_integer = (unsigned int*)buf->base;
+ unsigned int ipbin = ntohl(as_integer[4]);
+ unsigned char ip[4] = {0};
+ int i;
+ for (i = 0; i < 4; i++)
+ ip[i] = (ipbin >> i*8) & 0xff;
+ fprintf(stderr, "Offered IP %d.%d.%d.%d\n", ip[3], ip[2], ip[1], ip[0]);
+
+ free(buf->base);
+ uv_udp_recv_stop(req);
+}
+
+uv_buf_t make_discover_msg() {
+ uv_buf_t buffer;
+ alloc_buffer(NULL, 256, &buffer);
+ memset(buffer.base, 0, buffer.len);
+
+ // BOOTREQUEST
+ buffer.base[0] = 0x1;
+ // HTYPE ethernet
+ buffer.base[1] = 0x1;
+ // HLEN
+ buffer.base[2] = 0x6;
+ // HOPS
+ buffer.base[3] = 0x0;
+ // XID 4 bytes
+ buffer.base[4] = (unsigned int) random();
+ // SECS
+ buffer.base[8] = 0x0;
+ // FLAGS
+ buffer.base[10] = 0x80;
+ // CIADDR 12-15 is all zeros
+ // YIADDR 16-19 is all zeros
+ // SIADDR 20-23 is all zeros
+ // GIADDR 24-27 is all zeros
+ // CHADDR 28-43 is the MAC address, use your own
+ buffer.base[28] = 0xe4;
+ buffer.base[29] = 0xce;
+ buffer.base[30] = 0x8f;
+ buffer.base[31] = 0x13;
+ buffer.base[32] = 0xf6;
+ buffer.base[33] = 0xd4;
+ // SNAME 64 bytes zero
+ // FILE 128 bytes zero
+ // OPTIONS
+ // - magic cookie
+ buffer.base[236] = 99;
+ buffer.base[237] = 130;
+ buffer.base[238] = 83;
+ buffer.base[239] = 99;
+
+ // DHCP Message type
+ buffer.base[240] = 53;
+ buffer.base[241] = 1;
+ buffer.base[242] = 1; // DHCPDISCOVER
+
+ // DHCP Parameter request list
+ buffer.base[243] = 55;
+ buffer.base[244] = 4;
+ buffer.base[245] = 1;
+ buffer.base[246] = 3;
+ buffer.base[247] = 15;
+ buffer.base[248] = 6;
+
+ return buffer;
+}
+
+void on_send(uv_udp_send_t *req, int status) {
+ if (status) {
+ fprintf(stderr, "Send error %s\n", uv_strerror(status));
+ return;
+ }
+}
+
+int main() {
+ loop = uv_default_loop();
+
+ uv_udp_init(loop, &recv_socket);
+ struct sockaddr_in recv_addr;
+ uv_ip4_addr("0.0.0.0", 68, &recv_addr);
+ uv_udp_bind(&recv_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR);
+ uv_udp_recv_start(&recv_socket, alloc_buffer, on_read);
+
+ uv_udp_init(loop, &send_socket);
+ struct sockaddr_in broadcast_addr;
+ uv_ip4_addr("0.0.0.0", 0, &broadcast_addr);
+ uv_udp_bind(&send_socket, (const struct sockaddr *)&broadcast_addr, 0);
+ uv_udp_set_broadcast(&send_socket, 1);
+
+ uv_udp_send_t send_req;
+ uv_buf_t discover_msg = make_discover_msg();
+
+ struct sockaddr_in send_addr;
+ uv_ip4_addr("255.255.255.255", 67, &send_addr);
+ uv_udp_send(&send_req, &send_socket, &discover_msg, 1, (const struct sockaddr *)&send_addr, on_send);
+
+ return uv_run(loop, UV_RUN_DEFAULT);
+}
diff --git a/docs/code/uvcat/main.c b/docs/code/uvcat/main.c
new file mode 100644
index 00000000..b03b0944
--- /dev/null
+++ b/docs/code/uvcat/main.c
@@ -0,0 +1,63 @@
+#include <assert.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <uv.h>
+
+void on_read(uv_fs_t *req);
+
+uv_fs_t open_req;
+uv_fs_t read_req;
+uv_fs_t write_req;
+
+static char buffer[1024];
+
+static uv_buf_t iov;
+
+void on_write(uv_fs_t *req) {
+ if (req->result < 0) {
+ fprintf(stderr, "Write error: %s\n", uv_strerror((int)req->result));
+ }
+ else {
+ uv_fs_read(uv_default_loop(), &read_req, open_req.result, &iov, 1, -1, on_read);
+ }
+}
+
+void on_read(uv_fs_t *req) {
+ if (req->result < 0) {
+ fprintf(stderr, "Read error: %s\n", uv_strerror(req->result));
+ }
+ else if (req->result == 0) {
+ uv_fs_t close_req;
+ // synchronous
+ uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
+ }
+ else if (req->result > 0) {
+ iov.len = req->result;
+ uv_fs_write(uv_default_loop(), &write_req, 1, &iov, 1, -1, on_write);
+ }
+}
+
+void on_open(uv_fs_t *req) {
+ // The request passed to the callback is the same as the one the call setup
+ // function was passed.
+ assert(req == &open_req);
+ if (req->result >= 0) {
+ iov = uv_buf_init(buffer, sizeof(buffer));
+ uv_fs_read(uv_default_loop(), &read_req, req->result,
+ &iov, 1, -1, on_read);
+ }
+ else {
+ fprintf(stderr, "error opening file: %s\n", uv_strerror((int)req->result));
+ }
+}
+
+int main(int argc, char **argv) {
+ uv_fs_open(uv_default_loop(), &open_req, argv[1], O_RDONLY, 0, on_open);
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+ uv_fs_req_cleanup(&open_req);
+ uv_fs_req_cleanup(&read_req);
+ uv_fs_req_cleanup(&write_req);
+ return 0;
+}
diff --git a/docs/code/uvstop/main.c b/docs/code/uvstop/main.c
new file mode 100644
index 00000000..7aa53b76
--- /dev/null
+++ b/docs/code/uvstop/main.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <uv.h>
+
+int64_t counter = 0;
+
+void idle_cb(uv_idle_t *handle) {
+ printf("Idle callback\n");
+ counter++;
+
+ if (counter >= 5) {
+ uv_stop(uv_default_loop());
+ printf("uv_stop() called\n");
+ }
+}
+
+void prep_cb(uv_prepare_t *handle) {
+ printf("Prep callback\n");
+}
+
+int main() {
+ uv_idle_t idler;
+ uv_prepare_t prep;
+
+ uv_idle_init(uv_default_loop(), &idler);
+ uv_idle_start(&idler, idle_cb);
+
+ uv_prepare_init(uv_default_loop(), &prep);
+ uv_prepare_start(&prep, prep_cb);
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+ return 0;
+}
diff --git a/docs/code/uvtee/main.c b/docs/code/uvtee/main.c
new file mode 100644
index 00000000..6216c2eb
--- /dev/null
+++ b/docs/code/uvtee/main.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <uv.h>
+
+typedef struct {
+ uv_write_t req;
+ uv_buf_t buf;
+} write_req_t;
+
+uv_loop_t *loop;
+uv_pipe_t stdin_pipe;
+uv_pipe_t stdout_pipe;
+uv_pipe_t file_pipe;
+
+void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
+ *buf = uv_buf_init((char*) malloc(suggested_size), suggested_size);
+}
+
+void free_write_req(uv_write_t *req) {
+ write_req_t *wr = (write_req_t*) req;
+ free(wr->buf.base);
+ free(wr);
+}
+
+void on_stdout_write(uv_write_t *req, int status) {
+ free_write_req(req);
+}
+
+void on_file_write(uv_write_t *req, int status) {
+ free_write_req(req);
+}
+
+void write_data(uv_stream_t *dest, size_t size, uv_buf_t buf, uv_write_cb cb) {
+ write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t));
+ req->buf = uv_buf_init((char*) malloc(size), size);
+ memcpy(req->buf.base, buf.base, size);
+ uv_write((uv_write_t*) req, (uv_stream_t*)dest, &req->buf, 1, cb);
+}
+
+void read_stdin(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
+ if (nread < 0){
+ if (nread == UV_EOF){
+ // end of file
+ uv_close((uv_handle_t *)&stdin_pipe, NULL);
+ uv_close((uv_handle_t *)&stdout_pipe, NULL);
+ uv_close((uv_handle_t *)&file_pipe, NULL);
+ }
+ } else if (nread > 0) {
+ write_data((uv_stream_t *)&stdout_pipe, nread, *buf, on_stdout_write);
+ write_data((uv_stream_t *)&file_pipe, nread, *buf, on_file_write);
+ }
+
+ // OK to free buffer as write_data copies it.
+ if (buf->base)
+ free(buf->base);
+}
+
+int main(int argc, char **argv) {
+ loop = uv_default_loop();
+
+ uv_pipe_init(loop, &stdin_pipe, 0);
+ uv_pipe_open(&stdin_pipe, 0);
+
+ uv_pipe_init(loop, &stdout_pipe, 0);
+ uv_pipe_open(&stdout_pipe, 1);
+
+ uv_fs_t file_req;
+ int fd = uv_fs_open(loop, &file_req, argv[1], O_CREAT | O_RDWR, 0644, NULL);
+ uv_pipe_init(loop, &file_pipe, 0);
+ uv_pipe_open(&file_pipe, fd);
+
+ uv_read_start((uv_stream_t*)&stdin_pipe, alloc_buffer, read_stdin);
+
+ uv_run(loop, UV_RUN_DEFAULT);
+ return 0;
+}
diff --git a/docs/code/uvwget/main.c b/docs/code/uvwget/main.c
new file mode 100644
index 00000000..40186241
--- /dev/null
+++ b/docs/code/uvwget/main.c
@@ -0,0 +1,166 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <uv.h>
+#include <curl/curl.h>
+
+uv_loop_t *loop;
+CURLM *curl_handle;
+uv_timer_t timeout;
+
+typedef struct curl_context_s {
+ uv_poll_t poll_handle;
+ curl_socket_t sockfd;
+} curl_context_t;
+
+curl_context_t *create_curl_context(curl_socket_t sockfd) {
+ curl_context_t *context;
+
+ context = (curl_context_t*) malloc(sizeof *context);
+
+ context->sockfd = sockfd;
+
+ int r = uv_poll_init_socket(loop, &context->poll_handle, sockfd);
+ assert(r == 0);
+ context->poll_handle.data = context;
+
+ return context;
+}
+
+void curl_close_cb(uv_handle_t *handle) {
+ curl_context_t *context = (curl_context_t*) handle->data;
+ free(context);
+}
+
+void destroy_curl_context(curl_context_t *context) {
+ uv_close((uv_handle_t*) &context->poll_handle, curl_close_cb);
+}
+
+
+void add_download(const char *url, int num) {
+ char filename[50];
+ sprintf(filename, "%d.download", num);
+ FILE *file;
+
+ file = fopen(filename, "w");
+ if (file == NULL) {
+ fprintf(stderr, "Error opening %s\n", filename);
+ return;
+ }
+
+ CURL *handle = curl_easy_init();
+ curl_easy_setopt(handle, CURLOPT_WRITEDATA, file);
+ curl_easy_setopt(handle, CURLOPT_URL, url);
+ curl_multi_add_handle(curl_handle, handle);
+ fprintf(stderr, "Added download %s -> %s\n", url, filename);
+}
+
+void check_multi_info(void) {
+ char *done_url;
+ CURLMsg *message;
+ int pending;
+
+ while ((message = curl_multi_info_read(curl_handle, &pending))) {
+ switch (message->msg) {
+ case CURLMSG_DONE:
+ curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL,
+ &done_url);
+ printf("%s DONE\n", done_url);
+
+ curl_multi_remove_handle(curl_handle, message->easy_handle);
+ curl_easy_cleanup(message->easy_handle);
+ break;
+
+ default:
+ fprintf(stderr, "CURLMSG default\n");
+ abort();
+ }
+ }
+}
+
+void curl_perform(uv_poll_t *req, int status, int events) {
+ uv_timer_stop(&timeout);
+ int running_handles;
+ int flags = 0;
+ if (status < 0) flags = CURL_CSELECT_ERR;
+ if (!status && events & UV_READABLE) flags |= CURL_CSELECT_IN;
+ if (!status && events & UV_WRITABLE) flags |= CURL_CSELECT_OUT;
+
+ curl_context_t *context;
+
+ context = (curl_context_t*)req;
+
+ curl_multi_socket_action(curl_handle, context->sockfd, flags, &running_handles);
+ check_multi_info();
+}
+
+void on_timeout(uv_timer_t *req) {
+ int running_handles;
+ curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0, &running_handles);
+ check_multi_info();
+}
+
+void start_timeout(CURLM *multi, long timeout_ms, void *userp) {
+ if (timeout_ms <= 0)
+ timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in a bit */
+ uv_timer_start(&timeout, on_timeout, timeout_ms, 0);
+}
+
+int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *socketp) {
+ curl_context_t *curl_context;
+ if (action == CURL_POLL_IN || action == CURL_POLL_OUT) {
+ if (socketp) {
+ curl_context = (curl_context_t*) socketp;
+ }
+ else {
+ curl_context = create_curl_context(s);
+ curl_multi_assign(curl_handle, s, (void *) curl_context);
+ }
+ }
+
+ switch (action) {
+ case CURL_POLL_IN:
+ uv_poll_start(&curl_context->poll_handle, UV_READABLE, curl_perform);
+ break;
+ case CURL_POLL_OUT:
+ uv_poll_start(&curl_context->poll_handle, UV_WRITABLE, curl_perform);
+ break;
+ case CURL_POLL_REMOVE:
+ if (socketp) {
+ uv_poll_stop(&((curl_context_t*)socketp)->poll_handle);
+ destroy_curl_context((curl_context_t*) socketp);
+ curl_multi_assign(curl_handle, s, NULL);
+ }
+ break;
+ default:
+ abort();
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ loop = uv_default_loop();
+
+ if (argc <= 1)
+ return 0;
+
+ if (curl_global_init(CURL_GLOBAL_ALL)) {
+ fprintf(stderr, "Could not init cURL\n");
+ return 1;
+ }
+
+ uv_timer_init(loop, &timeout);
+
+ curl_handle = curl_multi_init();
+ curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket);
+ curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout);
+
+ while (argc-- > 1) {
+ add_download(argv[argc], argc);
+ }
+
+ uv_run(loop, UV_RUN_DEFAULT);
+ curl_multi_cleanup(curl_handle);
+ return 0;
+}