From 8d62c32b5923c786d2d5b1a4b2695a8e65afdf1a Mon Sep 17 00:00:00 2001 From: mohanson Date: Thu, 31 Oct 2024 09:07:57 +0800 Subject: [PATCH 1/7] Noting that process scheduling in ckb-vm is deterministic --- rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md index 01f5a48b..30fb2fac 100644 --- a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md +++ b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md @@ -14,7 +14,7 @@ This document describes the addition of the syscalls during the CKB Meepo hardfo ## Introduction -The design of the syscall spawn function draws inspiration from Unix and Linux, hence they share the same terminologies: process, pipe, and file descriptor. The spawn mechanism is used in ckb-vm to create new processes, which can then execute a different program or command independently of the parent process. +The design of the syscall spawn function draws inspiration from Unix and Linux, hence they share the same terminologies: process, pipe, and file descriptor. The spawn mechanism is used in ckb-vm to create new processes, which can then execute a different program or command independently of the parent process. It is worth noting that process scheduling in ckb-vm is deterministic. In the context of ckb-vm, a process represents the active execution of a RISC-V binary. This binary can be located within a cell. Additionally, a RISC-V binary can also be found within the witness during a syscall spawn. A pipe is established by associating two file descriptors, each linked to one of its ends. These file descriptors can't be duplicated and are exclusively owned by the process. Furthermore, the file descriptors can only be either read from or written to; they can't be both read from and written to simultaneously. From 4f2ed717207cafc3d1b04f97d83faf408e9f95c2 Mon Sep 17 00:00:00 2001 From: mohanson Date: Thu, 31 Oct 2024 09:22:58 +0800 Subject: [PATCH 2/7] Follow xxuejie's advice --- rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md index 30fb2fac..0d586a82 100644 --- a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md +++ b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md @@ -14,10 +14,15 @@ This document describes the addition of the syscalls during the CKB Meepo hardfo ## Introduction -The design of the syscall spawn function draws inspiration from Unix and Linux, hence they share the same terminologies: process, pipe, and file descriptor. The spawn mechanism is used in ckb-vm to create new processes, which can then execute a different program or command independently of the parent process. It is worth noting that process scheduling in ckb-vm is deterministic. +The design of the syscall spawn function draws inspiration from Unix and Linux, hence they share the same terminologies: process, pipe, and file descriptor. The spawn mechanism is used in ckb-vm to create new processes, which can then execute a different program or command independently of the parent process. In the context of ckb-vm, a process represents the active execution of a RISC-V binary. This binary can be located within a cell. Additionally, a RISC-V binary can also be found within the witness during a syscall spawn. A pipe is established by associating two file descriptors, each linked to one of its ends. These file descriptors can't be duplicated and are exclusively owned by the process. Furthermore, the file descriptors can only be either read from or written to; they can't be both read from and written to simultaneously. +It is worth noting that process scheduling in ckb-vm is deterministic, specifically: + +- For each hardfork version, the process scheduling will be deterministic, any indeterminism will be treated as critical / security bugs that requires immediate intervention +- However, based on real usage on chain, it is expected that future hardfork versions would improve the process scheduling workflow, hence making the behavior different across versions + We added 8 spawn-related syscalls and one block-related syscall, respectively: - [Spawn] From 2624349f20c7a541f30af8853616e355afea74e1 Mon Sep 17 00:00:00 2001 From: mohanson Date: Fri, 1 Nov 2024 11:06:01 +0800 Subject: [PATCH 3/7] Add deadlock section --- rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md index 0d586a82..0745c1ac 100644 --- a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md +++ b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md @@ -213,7 +213,25 @@ Five new error types added: - Error code 8: The maximum count of spawned processes has been reached. - Error code 9: The maximum count of created pipes has been reached. -It's possible for read/write/wait operations to wait for each other, leading to a deadlock state. In such cases, CKB-VM would throw an internal error and terminate immediately. +## Deadlock + +Deadlock is a situation where two or more processes are unable to proceed because they are each waiting for resources or conditions that can only be provided by another waiting process. In the context of this scheduler, where processes communicate via pipes and can enter various states, such as `Runnable`, `Wait`, `WaitForWrite`, `WaitForRead`, `Terminated`. In our scheduler, deadlock will occur if all unterminated processes are waiting and no process is in a runnable state. + +- The process enters the `Wait` state by calling the `wait()` +- The process enters the `WaitForWrite` state by calling the `write()` +- The process enters the `WaitForRead` state by calling the `read()` +- The process enters the `Runnable` when a process is created, or it get returned from `wait()`, `write()` and `read()`. +- The process enters the `Terminated` when a process is terminated. + +Here are the main scenarios that may lead to deadlock: + +0. **Circular Waiting**: If multiple processes are in the `Wait`, `WaitForWrite`, or `WaitForRead` states and are waiting on each other in a circular dependency, a deadlock can occur. For example, if: + - Process A is in `WaitForRead` for data from Process B + - Process B is in `WaitForRead` for Process A. Both processes will wait indefinitely, as each is waiting for the other to proceed. +0. **Buffer Limits**: If processes rely on pipes with limited buffer sizes and one process blocks on a `WaitForWrite` state because the pipe buffer is full, and the reader process is also blocked in a `WaitForRead` state (but on a different file descriptor), this can create a deadlock if neither can proceed: + - Process A want's read 10 bytes from fd0, and then read 10 bytes from fd1, and finally read 10 bytes from fd0. + - Process B write 20 bytes into fd0, and then write 10 bytes into fd1. + ## Cycles From 66d56d1b6a21bdb4c32b47fb57127fdb5e79a486 Mon Sep 17 00:00:00 2001 From: mohanson Date: Fri, 1 Nov 2024 11:12:46 +0800 Subject: [PATCH 4/7] typos --- rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md index 0745c1ac..47f84384 100644 --- a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md +++ b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md @@ -229,8 +229,8 @@ Here are the main scenarios that may lead to deadlock: - Process A is in `WaitForRead` for data from Process B - Process B is in `WaitForRead` for Process A. Both processes will wait indefinitely, as each is waiting for the other to proceed. 0. **Buffer Limits**: If processes rely on pipes with limited buffer sizes and one process blocks on a `WaitForWrite` state because the pipe buffer is full, and the reader process is also blocked in a `WaitForRead` state (but on a different file descriptor), this can create a deadlock if neither can proceed: - - Process A want's read 10 bytes from fd0, and then read 10 bytes from fd1, and finally read 10 bytes from fd0. - - Process B write 20 bytes into fd0, and then write 10 bytes into fd1. + - Process A wants to read 10 bytes from fd0, and then read 10 bytes from fd1, and finally read 10 bytes from fd0. + - Process B writes 20 bytes into fd0, and then write 10 bytes into fd1. ## Cycles From 864b234fcf9626dd85b05f5a6324299d4b1c8ddc Mon Sep 17 00:00:00 2001 From: mohanson Date: Tue, 12 Nov 2024 17:07:57 +0800 Subject: [PATCH 5/7] Repair scene description --- rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md index 47f84384..cc76429e 100644 --- a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md +++ b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md @@ -228,7 +228,7 @@ Here are the main scenarios that may lead to deadlock: 0. **Circular Waiting**: If multiple processes are in the `Wait`, `WaitForWrite`, or `WaitForRead` states and are waiting on each other in a circular dependency, a deadlock can occur. For example, if: - Process A is in `WaitForRead` for data from Process B - Process B is in `WaitForRead` for Process A. Both processes will wait indefinitely, as each is waiting for the other to proceed. -0. **Buffer Limits**: If processes rely on pipes with limited buffer sizes and one process blocks on a `WaitForWrite` state because the pipe buffer is full, and the reader process is also blocked in a `WaitForRead` state (but on a different file descriptor), this can create a deadlock if neither can proceed: +0. **Buffer Limits**: The pipe in ckb-vm is unbuffered. If one process blocks on a `WaitForWrite` state because the data is not fully read, and the reader process is also blocked in a `WaitForRead` state (but on a different file descriptor), this can create a deadlock if neither can proceed: - Process A wants to read 10 bytes from fd0, and then read 10 bytes from fd1, and finally read 10 bytes from fd0. - Process B writes 20 bytes into fd0, and then write 10 bytes into fd1. From 674c40b2bccfafc11fe7ba065536b54811464cb9 Mon Sep 17 00:00:00 2001 From: mohanson Date: Thu, 14 Nov 2024 13:56:54 +0800 Subject: [PATCH 6/7] Add running state --- rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md index cc76429e..1239094e 100644 --- a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md +++ b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md @@ -215,20 +215,21 @@ Five new error types added: ## Deadlock -Deadlock is a situation where two or more processes are unable to proceed because they are each waiting for resources or conditions that can only be provided by another waiting process. In the context of this scheduler, where processes communicate via pipes and can enter various states, such as `Runnable`, `Wait`, `WaitForWrite`, `WaitForRead`, `Terminated`. In our scheduler, deadlock will occur if all unterminated processes are waiting and no process is in a runnable state. +Deadlock is a situation where two or more processes are unable to proceed because they are each waiting for resources or conditions that can only be provided by another waiting process. In the context of this scheduler, where processes communicate via pipes and can enter various states, such as `Runnable`, `Running`, `Terminated`, `WaitForExit`, `WaitForRead`, `WaitForWrite`. In our scheduler, deadlock will occur if all unterminated processes are waiting and no process is in a runnable state. -- The process enters the `Wait` state by calling the `wait()` -- The process enters the `WaitForWrite` state by calling the `write()` -- The process enters the `WaitForRead` state by calling the `read()` - The process enters the `Runnable` when a process is created, or it get returned from `wait()`, `write()` and `read()`. +- The process enters the `Running` when a process is running. - The process enters the `Terminated` when a process is terminated. +- The process enters the `WaitForExit` state by calling the `wait()` +- The process enters the `WaitForRead` state by calling the `read()` +- The process enters the `WaitForWrite` state by calling the `write()` Here are the main scenarios that may lead to deadlock: 0. **Circular Waiting**: If multiple processes are in the `Wait`, `WaitForWrite`, or `WaitForRead` states and are waiting on each other in a circular dependency, a deadlock can occur. For example, if: - Process A is in `WaitForRead` for data from Process B - - Process B is in `WaitForRead` for Process A. Both processes will wait indefinitely, as each is waiting for the other to proceed. -0. **Buffer Limits**: The pipe in ckb-vm is unbuffered. If one process blocks on a `WaitForWrite` state because the data is not fully read, and the reader process is also blocked in a `WaitForRead` state (but on a different file descriptor), this can create a deadlock if neither can proceed: + - Process B is in `WaitForRead` for data from Process A. Both processes will wait indefinitely, as each is waiting for the other to proceed. +0. **Buffer Limits**: Essentially, it's another circular waiting. The pipe in ckb-vm is unbuffered. If one process blocks on a `WaitForWrite` state because the data is not fully read, and the reader process is also blocked in a `WaitForRead` state (but on a different file descriptor), this can create a deadlock if neither can proceed: - Process A wants to read 10 bytes from fd0, and then read 10 bytes from fd1, and finally read 10 bytes from fd0. - Process B writes 20 bytes into fd0, and then write 10 bytes into fd1. From e797d643f78f37fea0aa243e1b2edef64d11643b Mon Sep 17 00:00:00 2001 From: mohanson Date: Thu, 14 Nov 2024 16:20:01 +0800 Subject: [PATCH 7/7] Follow xxuejie's advice --- rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md index 1239094e..63f60b4c 100644 --- a/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md +++ b/rfcs/0050-vm-syscalls-3/0050-vm-syscalls-3.md @@ -217,19 +217,20 @@ Five new error types added: Deadlock is a situation where two or more processes are unable to proceed because they are each waiting for resources or conditions that can only be provided by another waiting process. In the context of this scheduler, where processes communicate via pipes and can enter various states, such as `Runnable`, `Running`, `Terminated`, `WaitForExit`, `WaitForRead`, `WaitForWrite`. In our scheduler, deadlock will occur if all unterminated processes are waiting and no process is in a runnable state. -- The process enters the `Runnable` when a process is created, or it get returned from `wait()`, `write()` and `read()`. -- The process enters the `Running` when a process is running. +- The process enters the `Runnable` when a process is created, or it's blocking condition is resolved. +- The process enters the `Running` when a process starts running. - The process enters the `Terminated` when a process is terminated. -- The process enters the `WaitForExit` state by calling the `wait()` -- The process enters the `WaitForRead` state by calling the `read()` -- The process enters the `WaitForWrite` state by calling the `write()` +- The process enters the `WaitForExit` state by calling the `wait()` on another process still running. +- The process enters the `WaitForRead` state by calling the `read()`. A process might not actually enter `WaitForRead` state by calling `read()`, if data are already available at the other end. It only enters this state when it wants data but data are not ready, in other words, it has a blocking condition. +- The process enters the `WaitForWrite` state by calling the `write()`. A process might not actually enter `WaitForRead` state by calling `write()`, if the other end is in `WaitForRead` state and is able to read all the data. -Here are the main scenarios that may lead to deadlock: +If multiple processes are in the `WaitForExit`, `WaitForWrite`, or `WaitForRead` states and are waiting on each other in a circular dependency, a deadlock can occur. Here are two examples: -0. **Circular Waiting**: If multiple processes are in the `Wait`, `WaitForWrite`, or `WaitForRead` states and are waiting on each other in a circular dependency, a deadlock can occur. For example, if: - - Process A is in `WaitForRead` for data from Process B - - Process B is in `WaitForRead` for data from Process A. Both processes will wait indefinitely, as each is waiting for the other to proceed. -0. **Buffer Limits**: Essentially, it's another circular waiting. The pipe in ckb-vm is unbuffered. If one process blocks on a `WaitForWrite` state because the data is not fully read, and the reader process is also blocked in a `WaitForRead` state (but on a different file descriptor), this can create a deadlock if neither can proceed: +1. A simple deadlock scenario, both processes are waiting for the other process to send data: + - Process A is in `WaitForRead` for data from process B + - Process B is in `WaitForRead` for data from process A. Both processes will wait indefinitely, as each is waiting for the other to proceed. + +2. Deadlock caused by unbuffered pipes. Note that the pipe in ckb-vm is unbuffered. If one process blocks on a `WaitForWrite` state because the data is not fully read, and the reader process is also blocked in a `WaitForRead` state (but on a different file descriptor), this can create a deadlock if neither can proceed: - Process A wants to read 10 bytes from fd0, and then read 10 bytes from fd1, and finally read 10 bytes from fd0. - Process B writes 20 bytes into fd0, and then write 10 bytes into fd1.