私はJSで単純なVMのようなものを作成することに取り組んでいます:
let sp = 0 // stack pointer
let m = [] // memory
initialize:
sp = 0
invoke:
m[sp] = m[sp - 1] // position in stack
m[sp + 1] = input[m[sp]] // method
m[sp + 2] = m[m[sp + 1]] // size
m[sp + 3] = 0 // link iterator
sp = sp + m[sp + 2] // new stack pointer
load_input:
if (m[sp + 3] < m[sp + 2]) {
m[sp] = input[m[sp + 3 + j]]
// do I need to Push the stack for these types of things?
continue load_input
} else {
continue invoke
}
まだ作業に近づいていませんが、いじくり回しています。あなたが何かヒントがあれば知りたいのですが、私は他のプログラミング言語構造ではなく配列のみを使用しようとしています。
しかし、私の質問...アプリケーションにeventsがある場合、またはそれ以外の場合はasynchronousの場合、コールスタックはどのように機能しますか?あなたはいくつかの応答を待って、1つの「パス」で10層の深さになります。次に、コードはどこか他の場所で評価を続け、20層の深さで、応答を待ちます。次に、3番目のレイヤーが5層の深さになります。では、これは一般的にどのように実装されますか?上記のコード例とコールスタックを作成する方法を考えると、非同期が発生したときに何をするのか迷っています。
私が正しく理解していれば、JavaScriptで fibers を実装しようとしています。 私が間違っている場合は、 JavaScriptイベントループのしくみ を確認するだけです。繊維の代わりに、あなたは約束をすることができます。
ご想像のとおり、スタックを分割して、複数の「パス」を保持できるようにする必要があります。つまり、各ファイバーの実行呼び出しスタックを保持する必要があります。
したがって、各ファイバーには独自のスタックがあり、状態があります。状態は、信号待ち、実行中、または実行準備ができている可能性があります。ファイバーは、作成されるとすぐに実行できる状態になります。そのためのステートマシンを作成します。
そして、実行可能な状態のファイバーを選択し、状態を実行中に変更して実行するスケジューラーが必要です。
最終的に、ファイバーは信号を生成するか、信号を待機します。どちらの場合も、制御をスケジューラーに戻し、状態は実行準備完了(yieldの場合)またはシグナルを待機するように変更されます。そして、スケジューラーは続行するために別のファイバーを選びに行きます。
ところで、私はVMなので、オペコードを読み取って実行するJavaScriptコードがあると想定しています。つまり、JavaScriptに移行するのではなく(少なくとも事前に)、代わりにJavaScriptでエミュレートします(それが理にかなっている場合)
スケジューラによると、それを実装する最も簡単な方法は round robin です。 Multilevel queue にも興味があるかもしれません。もちろん、他の解決策もあります。
もちろん、ファイバーは他のファイバーを作成できます。
そして、その信号について...信号を引き起こすいくつかの操作があります。ファイバーが降伏するタイミングを確認するか、信号が設定されたときにスケジューラーが制御を取り、状態を更新して戻ることができます。両方を実行することをお勧めします。ファイバーが信号を設定すると、信号が降り、スケジューラーが制御を取り、その信号を待機しているファイバーの状態を更新し、ファイバーを選択して続行します。
複合信号(すべてのセット、および任意のセット)を直接サポートできます。
ご覧のとおり、ファイバーが1セットあり、それぞれにスタックがあります。スケジューラーが実行を継続したい場合、スケジューラーはファイバーのスタックを現在のものとして設定する必要があります。スケジューラは、実行を再開する場所を認識し、それを実行する必要もあります。つまり、ファイバーが信号を生成するか、信号を待機する場合、その位置を保存する必要があります。
実行を再開する位置。スタック内にある場合とない場合があります。ただし、呼び出しではないことに注意してください。つまり、それをポップするリターンはありません。代わりに、スケジューラに制御を戻すときにプッシュし、スケジューラがファイバーの実行を続けるときにポップする必要があります。
ちなみに、ファイバーがコールを行うと、それも発生するようにできます。
(すべてのファイバーの)すべてのスタックを同じ構造に格納するとします。もちろん、それは可能です。ただし、各ファイバーにはポインターがあり、スタックは連続していません。配列にノードを割り当てるだけです。解放されたものを追跡して再利用できるようにする必要があります...おそらく、それは特別なヒープに似ていると思いますか?
JavaScriptでは、使用するスレッドは1つだけなので、上記で説明したスレッドの問題は実際にはありません。マルチスレッドの環境では、ファイバー専用のスレッドごとにスケジューラー(およびそのファイバー)を持つことができます。
以下は問題に関するものではありません。しかし、タイトルから最初に理解したのは、その代わりです。したがって、スレッドセーフスタックを実装する方法を探している方(コールスタックではなく、同じスタックを使用する複数のスレッド用)は、最初に次の実装を行うことをお勧めします。固定サイズのスレッドセーフすべての操作がアトミックな配列に基づくスタック。また、シングルリンクリストに基づくスレッドセーフスタック(これは、スピンによって解決できる競合状態を持っています)。次に、単一にリンクされた配列のリストに基づいて1つを実行します。これにより、配列を長くすることで競合状態の頻度を最小限に抑え、割り当ての数も削減します...配列をプールすることでさらに削減します