From 50829317619deaec5cbc2d3bc71ff5926afbaeaa Mon Sep 17 00:00:00 2001 From: Rama Aryasuta Pangestu Date: Thu, 4 Apr 2024 16:30:57 +0800 Subject: [PATCH] Add channel send and receive implementation --- src/go/ece/microcode/concurrent/channel.ts | 81 +++++++++++++++++-- src/go/ece/microcode/constructor.ts | 56 ++++++++++++- src/go/ece/microcode/lookup.ts | 10 +++ src/go/heap/heap.ts | 36 ++++++++- src/go/heap/types/auto_cast.ts | 28 +++++++ src/go/heap/types/context/thread.ts | 8 ++ src/go/heap/types/context/waiting_instance.ts | 15 ++++ src/go/heap/types/context/waker.ts | 9 +++ src/go/heap/types/control/chan_receive_i.ts | 22 +++++ .../heap/types/control/chan_receive_stmt.ts | 20 +++-- src/go/heap/types/control/make.ts | 68 ++++++++++++++++ src/go/heap/types/control/make_i.ts | 52 ++++++++++++ src/go/heap/types/tags.ts | 16 ++-- src/go/heap/types/user/channel.ts | 80 ++++++++++-------- 14 files changed, 444 insertions(+), 57 deletions(-) create mode 100644 src/go/heap/types/control/chan_receive_i.ts create mode 100644 src/go/heap/types/control/make.ts create mode 100644 src/go/heap/types/control/make_i.ts diff --git a/src/go/ece/microcode/concurrent/channel.ts b/src/go/ece/microcode/concurrent/channel.ts index f5d32d4..998f18c 100644 --- a/src/go/ece/microcode/concurrent/channel.ts +++ b/src/go/ece/microcode/concurrent/channel.ts @@ -2,8 +2,13 @@ import { Heap } from "../../../heap"; import { auto_cast } from "../../../heap/types/auto_cast"; import { ContextScheduler } from "../../../heap/types/context/scheduler"; import { ContextThread } from "../../../heap/types/context/thread"; +import { ControlChanReceive } from "../../../heap/types/control/chan_receive"; +import { ControlChanReceiveI } from "../../../heap/types/control/chan_receive_i"; +import { ControlChanReceiveStmt } from "../../../heap/types/control/chan_receive_stmt"; import { ControlChanSend } from "../../../heap/types/control/chan_send"; import { ControlChanSendI } from "../../../heap/types/control/chan_send_i"; +import { ControlName } from "../../../heap/types/control/name"; +import { ControlNameAddress } from "../../../heap/types/control/name_address"; import { PrimitiveNil } from "../../../heap/types/primitive/nil"; import { UserChannel } from "../../../heap/types/user/channel"; import { UserVariable } from "../../../heap/types/user/variable"; @@ -15,14 +20,17 @@ function evaluate_chan_send( scheduler: ContextScheduler ) { const cmd_object = auto_cast(heap, cmd) as ControlChanSend; - const name = cmd_object.get_name_address(); + const name = cmd_object.get_name_address() as ControlName; const value = cmd_object.get_value_address(); const chan_send_i = ControlChanSendI.allocate(heap); thread.control().push(chan_send_i); heap.free_object(chan_send_i); - thread.control().push(name.address); + const name_address = ControlNameAddress.allocate(heap, name.get_name()); + thread.control().push(name_address); + heap.free_object(name_address); + thread.control().push(value.address); scheduler.enqueue(thread); @@ -37,8 +45,6 @@ function evaluate_chan_send_i( const name = auto_cast(heap, thread.stash().pop()) as UserVariable; const value = auto_cast(heap, thread.stash().pop()); - console.log(name.stringify()) - if (name.get_value().is_nil()) { throw new Error("evaluate_chan_send_i: nil channel"); } @@ -55,4 +61,69 @@ function evaluate_chan_send_i( value.free(); } -export { evaluate_chan_send, evaluate_chan_send_i }; +function evaluate_chan_receive( + cmd: number, + heap: Heap, + thread: ContextThread, + scheduler: ContextScheduler +) { + const cmd_object = auto_cast(heap, cmd) as ControlChanReceive; + const name = cmd_object.get_name_address() as ControlName; + + const chan_receive_i = ControlChanReceiveI.allocate(heap); + thread.control().push(chan_receive_i); + heap.free_object(chan_receive_i); + + const name_address = ControlNameAddress.allocate(heap, name.get_name()); + thread.control().push(name_address); + heap.free_object(name_address); + + scheduler.enqueue(thread); +} + +function evaluate_chan_receive_stmt( + cmd: number, + heap: Heap, + thread: ContextThread, + scheduler: ContextScheduler +) { + const cmd_object = auto_cast(heap, cmd) as ControlChanReceiveStmt; + const pop_i_cmd = heap.allocate_any({ tag: "pop_i" }); + + thread.control().push(pop_i_cmd); + thread.control().push(cmd_object.get_body_address().address); + + heap.free_object(pop_i_cmd); + + scheduler.enqueue(thread); +} + +function evaluate_chan_receive_i( + cmd: number, + heap: Heap, + thread: ContextThread, + scheduler: ContextScheduler +) { + const name = auto_cast(heap, thread.stash().pop()) as UserVariable; + + if (name.get_value().is_nil()) { + throw new Error("evaluate_chan_receive_i: nil channel"); + } + + // Note: receive() is responsible for enqueueing the thread. + (name.get_value() as UserChannel).recv( + thread, + scheduler, + PrimitiveNil.allocate_default(heap) + ); + + name.free(); +} + +export { + evaluate_chan_send, + evaluate_chan_send_i, + evaluate_chan_receive, + evaluate_chan_receive_stmt, + evaluate_chan_receive_i, +}; diff --git a/src/go/ece/microcode/constructor.ts b/src/go/ece/microcode/constructor.ts index bef60f2..7d02dad 100644 --- a/src/go/ece/microcode/constructor.ts +++ b/src/go/ece/microcode/constructor.ts @@ -3,7 +3,7 @@ import { ContextEnv } from '../../heap/types/context/env'; import { ContextStash } from '../../heap/types/context/stash'; import { Heap } from '../../heap'; import { auto_cast } from '../../heap/types/auto_cast'; -import { TAG_USER_type_array, TAG_USER_type_channel, TAG_USER_type_function, TAG_USER_type_struct_decl } from '../../heap/types/tags'; +import { TAG_USER_type_array, TAG_USER_type_channel, TAG_USER_type_function, TAG_USER_type_slice, TAG_USER_type_struct_decl } from '../../heap/types/tags'; import { ComplexArray } from '../../heap/types/complex/array'; import { UserTypeArray } from '../../heap/types/user/type/array'; import { UserVariable } from '../../heap/types/user/variable'; @@ -12,6 +12,10 @@ import { UserTypeStructDecl } from '../../heap/types/user/type/struct_decl'; import { UserTypeStruct } from '../../heap/types/user/type/struct'; import { UserStruct } from '../../heap/types/user/struct'; import { ControlDefaultMake } from '../../heap/types/control/default_make'; +import { ControlMake } from '../../heap/types/control/make'; +import { ControlMakeI } from '../../heap/types/control/make_i'; +import { PrimitiveInt32 } from '../../heap/types/primitive/int32'; +import { UserChannel } from '../../heap/types/user/channel'; function evaluate_default_make(cmd: number, heap: Heap, C: ContextControl, S: ContextStash, E: ContextEnv): void { const make_cmd = auto_cast(heap, cmd) as ControlDefaultMake; @@ -51,6 +55,54 @@ function evaluate_default_make(cmd: number, heap: Heap, C: ContextControl, S: Co heap.free_object(nil_address); break; } + default: { + throw new Error("evaluate_default_make: Invalid type"); + } + } +} + +function evaluate_make(cmd: number, heap: Heap, C: ContextControl, S: ContextStash, E: ContextEnv): void { + const make_cmd = auto_cast(heap, cmd) as ControlMake; + + const make_i_cmd = heap.allocate_any({ + tag: "make_i", + type: make_cmd.get_type(), + num_args: make_cmd.get_number_of_args(), + }); + C.push(make_i_cmd); + heap.free_object(make_i_cmd); + + for (let i = 0; i < make_cmd.get_number_of_args(); i++) { + C.push(make_cmd.get_arg_address(i).address); + } +} + +function evaluate_make_i(cmd: number, heap: Heap, C: ContextControl, S: ContextStash, E: ContextEnv): void { + const make_cmd = auto_cast(heap, cmd) as ControlMakeI; + const type = make_cmd.get_type(); + const num_args = make_cmd.get_number_of_arguments(); + + switch (type.get_tag()) { + case TAG_USER_type_channel: { + if (num_args > 1) { + throw new Error("evaluate_make: channel expects 1 or 2 arguments"); + } + let buffer_size: number; + if (num_args === 0) { + buffer_size = 0; + } else { + const buffer_size_value = auto_cast(heap, S.pop()) as PrimitiveInt32; + buffer_size = buffer_size_value.get_value(); + buffer_size_value.free(); + } + const channel = new UserChannel(heap, UserChannel.allocate(heap, buffer_size, type)); + S.push(channel.address); + channel.free(); + break; + } + case TAG_USER_type_slice: { + throw new Error("evaluate_make: Not implemented"); + } default: { throw new Error("evaluate_make: Invalid type"); } @@ -104,6 +156,8 @@ function evaluate_constructor_i(cmd: number, heap: Heap, C: ContextControl, S: C export { evaluate_default_make, + evaluate_make, + evaluate_make_i, evaluate_constructor, evaluate_constructor_i, }; \ No newline at end of file diff --git a/src/go/ece/microcode/lookup.ts b/src/go/ece/microcode/lookup.ts index f657edc..1454835 100644 --- a/src/go/ece/microcode/lookup.ts +++ b/src/go/ece/microcode/lookup.ts @@ -86,6 +86,10 @@ function lookup_microcode_sequential(tag: number): Function { return control_var.evaluate_name_address; case tags.TAG_CONTROL_default_make: return constructor.evaluate_default_make; + case tags.TAG_CONTROL_make: + return constructor.evaluate_make; + case tags.TAG_CONTROL_make_i: + return constructor.evaluate_make_i; case tags.TAG_CONTROL_index: return array.evaluate_index; case tags.TAG_CONTROL_index_i: @@ -125,6 +129,12 @@ function lookup_microcode(tag: number): Function { return concurrent_channel.evaluate_chan_send; case tags.TAG_CONTROL_chan_send_i: return concurrent_channel.evaluate_chan_send_i; + case tags.TAG_CONTROL_chan_receive: + return concurrent_channel.evaluate_chan_receive; + case tags.TAG_CONTROL_chan_receive_stmt: + return concurrent_channel.evaluate_chan_receive_stmt; + case tags.TAG_CONTROL_chan_receive_i: + return concurrent_channel.evaluate_chan_receive_i; default: return ( cmd: number, diff --git a/src/go/heap/heap.ts b/src/go/heap/heap.ts index cfdd496..2a11355 100644 --- a/src/go/heap/heap.ts +++ b/src/go/heap/heap.ts @@ -70,6 +70,8 @@ import { ControlIndexI } from "./types/control/index_i"; import { ControlLiteral } from "./types/control/literal"; import { ControlLogicalI } from "./types/control/logical_i"; import { ControlLogicalImmI } from "./types/control/logical_imm_i"; +import { ControlMake } from "./types/control/make"; +import { ControlMakeI } from "./types/control/make_i"; import { ControlMember } from "./types/control/member"; import { ControlMemberAddress } from "./types/control/member_address"; import { ControlMemberAddressI } from "./types/control/member_address_i"; @@ -169,7 +171,9 @@ import { TAGSTRING_CONTROL_method_member, TAGSTRING_CONTROL_member_address_i, TAGSTRING_CONTROL_push_i, - TAGSTRING_CONTROL_default_make + TAGSTRING_CONTROL_default_make, + TAGSTRING_CONTROL_make_i, + TAGSTRING_CONTROL_make } from "./types/tags"; import { UserType } from "./types/user/type"; import { UserTypeArray } from "./types/user/type/array"; @@ -937,6 +941,28 @@ class Heap { return ControlDefaultMake.allocate(this, obj.type, obj.args); } + /** + * CONTROL_make + * Fields : number of children + * Children : + * - 4 bytes address of the type (type) + * - 4 bytes * num_arguments address of the arguments (expression) + */ + public allocate_CONTROL_make(obj: { tag: string, type: any, args: any[] }): number { + return ControlMake.allocate(this, obj.type, obj.args); + } + + /** + * CONTROL_make_i + * Fields : number of children + * Children : + * - 4 bytes address of the type (USER_type) + * - 4 bytes number of arguments (PRIMITIVE_int32) + */ + public allocate_CONTROL_make_i(obj: { tag: string, type: UserType, num_args: number }): number { + return ControlMakeI.allocate(this, obj.type, obj.num_args); + } + /** * CONTROL_index * Fields : number of children @@ -1024,8 +1050,8 @@ class Heap { * Children : * - 4 bytes address of the name of the channel (COMPLEX_string) */ - public allocate_CONTROL_chan_receive_stmt(obj: { tag: string, name: any }): number { - return ControlChanReceiveStmt.allocate(this, obj.name); + public allocate_CONTROL_chan_receive_stmt(obj: { tag: string, body: any }): number { + return ControlChanReceiveStmt.allocate(this, obj.body); } /** @@ -1320,6 +1346,8 @@ class Heap { return this.allocate_CONTROL_name_address(obj); case TAGSTRING_CONTROL_default_make: return this.allocate_CONTROL_default_make(obj); + case TAGSTRING_CONTROL_make: + return this.allocate_CONTROL_make(obj); case TAGSTRING_CONTROL_index: return this.allocate_CONTROL_index(obj); case TAGSTRING_CONTROL_index_i: @@ -1332,6 +1360,8 @@ class Heap { return this.allocate_CONTROL_constructor(obj); case TAGSTRING_CONTROL_constructor_i: return this.allocate_CONTROL_constructor_i(obj); + case TAGSTRING_CONTROL_make_i: + return this.allocate_CONTROL_make_i(obj); case TAGSTRING_CONTROL_chan_send: return this.allocate_CONTROL_chan_send(obj); case TAGSTRING_CONTROL_chan_receive: diff --git a/src/go/heap/types/auto_cast.ts b/src/go/heap/types/auto_cast.ts index c1089ef..a3e3cbb 100644 --- a/src/go/heap/types/auto_cast.ts +++ b/src/go/heap/types/auto_cast.ts @@ -8,8 +8,11 @@ import { ComplexPointer } from "./complex/pointer"; import { ComplexString } from "./complex/string"; import { ContextControl } from "./context/control"; import { ContextEnv } from "./context/env"; +import { ContextScheduler } from "./context/scheduler"; import { ContextStash } from "./context/stash"; import { ContextThread } from "./context/thread"; +import { ContextWaitingInstance } from "./context/waiting_instance"; +import { ContextWaker } from "./context/waker"; import { ControlIndex } from "./control"; import { ControlAssign } from "./control/assign"; import { ControlAssignI } from "./control/assign_i"; @@ -21,6 +24,7 @@ import { ControlCall } from "./control/call"; import { ControlCallI } from "./control/call_i"; import { ControlCallStmt } from "./control/call_stmt"; import { ControlChanReceive } from "./control/chan_receive"; +import { ControlChanReceiveI } from "./control/chan_receive_i"; import { ControlChanReceiveStmt } from "./control/chan_receive_stmt"; import { ControlChanSend } from "./control/chan_send"; import { ControlChanSendI } from "./control/chan_send_i"; @@ -40,6 +44,8 @@ import { ControlIndexAddressI } from "./control/index_address_i"; import { ControlIndexI } from "./control/index_i"; import { ControlLogicalI } from "./control/logical_i"; import { ControlLogicalImmI } from "./control/logical_imm_i"; +import { ControlMake } from "./control/make"; +import { ControlMakeI } from "./control/make_i"; import { ControlMember } from "./control/member"; import { ControlMemberAddress } from "./control/member_address"; import { ControlMemberAddressI } from "./control/member_address_i"; @@ -155,7 +161,15 @@ import { TAG_CONTROL_push_i, TAG_CONTROL_chan_send_i, TAG_CONTROL_default_make, + TAG_CONTROL_make, + TAG_CONTROL_make_i, + TAG_USER_channel, + TAG_CONTEXT_scheduler, + TAG_CONTEXT_waker, + TAG_CONTEXT_waiting_instance, + TAG_CONTROL_chan_receive_i, } from "./tags" +import { UserChannel } from "./user/channel"; import { UserStruct } from "./user/struct"; import { UserTypeArray } from "./user/type/array"; import { UserTypeBool } from "./user/type/bool"; @@ -272,6 +286,10 @@ function auto_cast(heap: Heap, address: number): HeapObject { return new ControlNameAddress(heap, address); case TAG_CONTROL_default_make: return new ControlDefaultMake(heap, address); + case TAG_CONTROL_make: + return new ControlMake(heap, address); + case TAG_CONTROL_make_i: + return new ControlMakeI(heap, address); case TAG_CONTROL_index: return new ControlIndex(heap, address); case TAG_CONTROL_index_i: @@ -290,6 +308,8 @@ function auto_cast(heap: Heap, address: number): HeapObject { return new ControlChanSendI(heap, address); case TAG_CONTROL_chan_receive: return new ControlChanReceive(heap, address); + case TAG_CONTROL_chan_receive_i: + return new ControlChanReceiveI(heap, address); case TAG_CONTROL_chan_receive_stmt: return new ControlChanReceiveStmt(heap, address); case TAG_CONTROL_struct: @@ -316,10 +336,18 @@ function auto_cast(heap: Heap, address: number): HeapObject { return new ContextStash(heap, address); case TAG_CONTEXT_env: return new ContextEnv(heap, address); + case TAG_CONTEXT_scheduler: + return new ContextScheduler(heap, address); + case TAG_CONTEXT_waker: + return new ContextWaker(heap, address); + case TAG_CONTEXT_waiting_instance: + return new ContextWaitingInstance(heap, address); case TAG_USER_variable: return new UserVariable(heap, address); case TAG_USER_struct: return new UserStruct(heap, address); + case TAG_USER_channel: + return new UserChannel(heap, address); case TAG_USER_type_array: return new UserTypeArray(heap, address); case TAG_USER_type_bool: diff --git a/src/go/heap/types/context/thread.ts b/src/go/heap/types/context/thread.ts index 833d367..f2435fb 100644 --- a/src/go/heap/types/context/thread.ts +++ b/src/go/heap/types/context/thread.ts @@ -56,6 +56,10 @@ class ContextThread extends HeapObject { } public fork(): ContextThread { + if (this.get_tag() !== TAG_CONTEXT_thread) { + throw new Error("ContextThread.fork: Invalid tag"); + } + const forked_address = this.heap.allocate_object(TAG_CONTEXT_thread, 2, 3); this.heap.set_cannnot_be_freed(forked_address, true); this.heap.set_field(forked_address, 1, ContextThread.generate_thread_id()); @@ -74,6 +78,10 @@ class ContextThread extends HeapObject { } public createWaker(): ContextWaker { + if (this.get_tag() !== TAG_CONTEXT_thread) { + throw new Error("ContextThread.createWaker: Invalid tag"); + } + const w = ContextWaker.allocate(this.heap, this); return new ContextWaker(this.heap, w); } diff --git a/src/go/heap/types/context/waiting_instance.ts b/src/go/heap/types/context/waiting_instance.ts index da3c21f..702904f 100644 --- a/src/go/heap/types/context/waiting_instance.ts +++ b/src/go/heap/types/context/waiting_instance.ts @@ -16,24 +16,39 @@ import { ContextWaker } from "./waker"; class ContextWaitingInstance extends HeapObject { public get_waker(): ContextWaker { + if (this.get_tag() !== TAG_CONTEXT_waiting_instance) { + throw new Error("ContextWaitingInstance.get_waker: invalid object tag"); + } return new ContextWaker(this.heap, this.get_child(0)); } public get_value(): HeapObject { + if (this.get_tag() !== TAG_CONTEXT_waiting_instance) { + throw new Error("ContextWaitingInstance.get_waker: invalid object tag"); + } return auto_cast(this.heap, this.get_child(1)); } public set_value(value: HeapObject): void { + if (this.get_tag() !== TAG_CONTEXT_waiting_instance) { + throw new Error("ContextWaitingInstance.get_waker: invalid object tag"); + } const old_value = this.get_value(); this.set_child(1, value.reference().address); old_value.free(); } public get_body(): HeapObject { + if (this.get_tag() !== TAG_CONTEXT_waiting_instance) { + throw new Error("ContextWaitingInstance.get_waker: invalid object tag"); + } return auto_cast(this.heap, this.get_child(2)); } public set_body(body: HeapObject): void { + if (this.get_tag() !== TAG_CONTEXT_waiting_instance) { + throw new Error("ContextWaitingInstance.get_waker: invalid object tag"); + } const old_body = this.get_body(); this.set_child(2, body.reference().address); old_body.free(); diff --git a/src/go/heap/types/context/waker.ts b/src/go/heap/types/context/waker.ts index 34b980c..b4ff85b 100644 --- a/src/go/heap/types/context/waker.ts +++ b/src/go/heap/types/context/waker.ts @@ -14,10 +14,16 @@ import { ContextThread } from "./thread"; class ContextWaker extends HeapObject { public isEmpty(): boolean { + if (this.get_tag() !== TAG_CONTEXT_waker) { + throw new Error("ContextWaker.isEmpty: invalid object tag"); + } return this.get_child(0) === PrimitiveNil.allocate(); } public get_thread(): ContextThread { + if (this.get_tag() !== TAG_CONTEXT_waker) { + throw new Error("ContextWaker.isEmpty: invalid object tag"); + } return new ContextThread(this.heap, this.get_child(0)); } @@ -26,6 +32,9 @@ class ContextWaker extends HeapObject { * @returns whether a thread was woken */ public wake(scheduler: ContextScheduler): boolean { + if (this.get_tag() !== TAG_CONTEXT_waker) { + throw new Error("ContextWaker.isEmpty: invalid object tag"); + } const thread = new ContextThread(this.heap, this.get_child(0)); if (!thread.is_nil()) { this.set_child(0, PrimitiveNil.allocate()); diff --git a/src/go/heap/types/control/chan_receive_i.ts b/src/go/heap/types/control/chan_receive_i.ts new file mode 100644 index 0000000..9122167 --- /dev/null +++ b/src/go/heap/types/control/chan_receive_i.ts @@ -0,0 +1,22 @@ +/** + * CONTROL_chan_receive_i + */ + +import { Heap } from "../../heap"; +import { HeapObject } from "../objects"; +import { TAG_CONTROL_chan_receive_i } from "../tags"; + +class ControlChanReceiveI extends HeapObject { + public static allocate(heap: Heap) { + const address = heap.allocate_object(TAG_CONTROL_chan_receive_i, 0, 0); + return address; + } + + public stringify_i(): string { + let result = ""; + result += this.address.toString() + " (chan_receive_i): "; + return result; + } +} + +export { ControlChanReceiveI }; diff --git a/src/go/heap/types/control/chan_receive_stmt.ts b/src/go/heap/types/control/chan_receive_stmt.ts index 9bc715a..cd78b1b 100644 --- a/src/go/heap/types/control/chan_receive_stmt.ts +++ b/src/go/heap/types/control/chan_receive_stmt.ts @@ -2,39 +2,37 @@ * CONTROL_chan_receive_stmt * Fields : number of children * Children : - * - 4 bytes address of the name of the channel (COMPLEX_string) + * - 4 bytes address (CONTROL_chan_receive) */ import { Heap } from "../../heap"; import { auto_cast } from "../auto_cast"; import { HeapObject } from "../objects"; import { TAG_CONTROL_chan_receive_stmt } from "../tags"; +import { ControlChanReceive } from "./chan_receive"; class ControlChanReceiveStmt extends HeapObject { - public get_name_address(): HeapObject { - return auto_cast(this.heap, this.get_child(0)); + public get_body_address(): ControlChanReceive { + return auto_cast(this.heap, this.get_child(0)) as ControlChanReceive; } - public static allocate(heap: Heap, name: any) { + public static allocate(heap: Heap, body: any) { const address = heap.allocate_object(TAG_CONTROL_chan_receive_stmt, 1, 1); heap.set_cannnot_be_freed(address, true); - const name_address = heap.allocate_any(name); - heap.set_cannnot_be_freed(name_address, true); - - heap.set_child(address, 0, name_address); + const body_address = heap.allocate_any(body); + heap.set_child(address, 0, body_address); // Unmark cannot-be-free heap.set_cannnot_be_freed(address, false); - heap.set_cannnot_be_freed(name_address, false); return address; } public stringify_i(): string { let result = ""; - result += this.address.toString() + " (chan_receive): "; - result += this.get_name_address().stringify(); + result += this.address.toString() + " (chan_receive_stmt): "; + result += this.get_body_address().stringify(); return result; } } diff --git a/src/go/heap/types/control/make.ts b/src/go/heap/types/control/make.ts new file mode 100644 index 0000000..196f9e0 --- /dev/null +++ b/src/go/heap/types/control/make.ts @@ -0,0 +1,68 @@ +/** + * CONTROL_make + * Fields : + * - number of children + * Children : + * - 4 bytes address of the type (type) + * - 4 bytes * num_arguments address of the arguments (expression) + */ + +import { Heap } from "../../heap"; +import { auto_cast } from "../auto_cast"; +import { HeapObject } from "../objects"; +import { TAG_CONTROL_make } from "../tags"; + +class ControlMake extends HeapObject { + public get_type_address(): number { + return this.get_child(0); + } + + public get_type(): HeapObject { + return auto_cast(this.heap, this.get_type_address()); + } + + public get_arg_address(index: number): HeapObject { + return auto_cast(this.heap, this.get_child(index + 1)); + } + + public get_number_of_args(): number { + return this.get_number_of_children() - 1; + } + + public static allocate(heap: Heap, type: any, args: any[]): number { + const address = heap.allocate_object(TAG_CONTROL_make, 2, 1 + args.length); + heap.set_cannnot_be_freed(address, true); + + const type_address = heap.allocate_any(type); + heap.set_cannnot_be_freed(type_address, true); + + heap.set_child(address, 0, type_address); + for (let i = 0; i < args.length; i++) { + const arg_address = heap.allocate_any(args[i]); + heap.set_child(address, i + 1, arg_address); + } + + // Unmark cannot-be-free + heap.set_cannnot_be_freed(address, false); + heap.set_cannnot_be_freed(type_address, false); + + return address; + } + + public stringify_i(): string { + let result = ""; + result += this.address.toString() + " (make): "; + result += this.get_type().stringify(); + result += "("; + for (let i = 0; i < this.get_number_of_args(); i++) { + if (i > 0) { + result += ", "; + } + result += this.get_arg_address(i).stringify(); + } + result += ")"; + return result; + } +} + +export { ControlMake }; diff --git a/src/go/heap/types/control/make_i.ts b/src/go/heap/types/control/make_i.ts new file mode 100644 index 0000000..3126018 --- /dev/null +++ b/src/go/heap/types/control/make_i.ts @@ -0,0 +1,52 @@ +/** + * CONTROL_make_i + * Fields : + * - number of children + * Children : + * - 4 bytes address of the type (USER_type) + * - 4 bytes number of arguments (PRIMITIVE_int32) + */ + +import { Heap } from "../../../heap"; +import { auto_cast } from "../auto_cast"; +import { HeapObject } from "../objects"; +import { PrimitiveInt32 } from "../primitive/int32"; +import { TAG_CONTROL_make_i } from "../tags"; +import { UserType } from "../user/type"; + +class ControlMakeI extends HeapObject { + public get_type(): UserType { + return auto_cast(this.heap, this.get_child(0)) as UserType; + } + + public get_number_of_arguments(): number { + return ( + auto_cast(this.heap, this.get_child(1)) as PrimitiveInt32 + ).get_value(); + } + + public static allocate(heap: Heap, type: UserType, num_args: number): number { + const address = heap.allocate_object(TAG_CONTROL_make_i, 1, 2); + heap.set_cannnot_be_freed(address, true); + + heap.set_child(address, 0, type.reference().address); + + const number_address = heap.allocate_PRIMITIVE_int32(num_args); + heap.set_child(address, 1, number_address); + + // Unmark cannot be freed + heap.set_cannnot_be_freed(address, false); + + return address; + } + + public stringify_i(): string { + let result = ""; + result += this.address.toString() + " (make_i): "; + result += this.get_type().stringify() + " "; + result += this.get_number_of_arguments().toString(); + return result; + } +} + +export { ControlMakeI }; diff --git a/src/go/heap/types/tags.ts b/src/go/heap/types/tags.ts index 9536d00..f8e5473 100644 --- a/src/go/heap/types/tags.ts +++ b/src/go/heap/types/tags.ts @@ -28,6 +28,8 @@ export const TAG_CONTROL_assign_i = 0x8006; // 1000 0000 0000 0110 export const TAG_CONTROL_index_i = 0x8007; // 1000 0000 0000 0111 export const TAG_CONTROL_index_address_i = 0x8008; // 1000 0000 0000 1000 export const TAG_CONTROL_chan_send_i = 0x8009; // 1000 0000 0000 1001 +export const TAG_CONTROL_chan_receive_i = 0x800A; // 1000 0000 0000 1010 + export const TAG_CONTROL_name = 0xC001; // 1100 0000 0000 0001 export const TAG_CONTROL_literal = 0xC002; // 1100 0000 0000 0010 @@ -73,12 +75,14 @@ export const TAG_CONTROL_constructor = 0xC02C; // 1100 0000 0010 1100 export const TAG_CONTROL_constructor_i = 0xC02D; // 1100 0000 0010 1101 export const TAG_CONTROL_chan_send = 0xC02E; // 1100 0000 0010 1110 export const TAG_CONTROL_chan_receive = 0xC030; // 1100 0000 0011 0000 -export const TAG_CONTROL_chan_receive_i = 0xC031; // 1100 0000 0011 0001 export const TAG_CONTROL_chan_receive_stmt = 0xC032; // 1100 0000 0011 0010 export const TAG_CONTROL_method = 0xC033; // 1100 0000 0011 0011 export const TAG_CONTROL_method_member = 0xC034; // 1100 0000 0011 0100 export const TAG_CONTROL_member_address_i = 0xC035; // 1100 0000 0011 0101 export const TAG_CONTROL_push_i = 0xC036; // 1100 0000 0011 0110 +export const TAG_CONTROL_make = 0xC037; // 1100 0000 0011 0111 +export const TAG_CONTROL_make_i = 0xC038; // 1100 0000 0011 1000 + export const TAG_CONTROL_select_i = 0xC033; // 1100 0000 0011 0011 export const TAG_CONTROL_select_case_i = 0xC034; // 1100 0000 0011 0100 @@ -96,10 +100,6 @@ export const TAG_CONTEXT_scheduler = 0xC204; // 1100 0010 0000 0100 export const TAG_CONTEXT_waker = 0xC205; // 1100 0010 0000 0101 export const TAG_CONTEXT_waiting_instance = 0xC206; // 1100 0010 0000 0110 -export const TAG_USER_variable = 0xC301; // 1100 0011 0000 0001 -export const TAG_USER_struct = 0xC302; // 1100 0011 0000 0010 -export const TAG_USER_channel = 0xC303; // 1100 0011 0000 0011 - export const TAG_USER_type_struct = 0xC300; // 1100 0011 0000 0000 export const TAG_USER_type_int32 = 0xC303; // 1100 0011 0000 0011 export const TAG_USER_type_float32 = 0xC304; // 1100 0011 0000 0100 @@ -114,6 +114,10 @@ export const TAG_USER_type_builtin = 0xC30C; // 1100 0011 0000 1100 export const TAG_USER_type_struct_decl = 0xC30D; // 1100 0011 0000 1101 export const TAG_USER_type_method = 0xC30E; // 1100 0011 0000 1110 +export const TAG_USER_variable = 0xC311; // 1100 0011 0000 0001 +export const TAG_USER_struct = 0xC312; // 1100 0011 0000 0010 +export const TAG_USER_channel = 0xC313; // 1100 0011 0000 0011 + export const TAGSTRING_PRIMITIVE_nil = "nil"; export const TAGSTRING_PRIMITIVE_bool = "bool"; export const TAGSTRING_PRIMITIVE_int32 = "int32"; @@ -150,6 +154,8 @@ export const TAGSTRING_CONTROL_member = "member"; export const TAGSTRING_CONTROL_member_address = "member-address"; export const TAGSTRING_CONTROL_name_address = "name-address"; export const TAGSTRING_CONTROL_default_make = "default-make"; +export const TAGSTRING_CONTROL_make = "make"; +export const TAGSTRING_CONTROL_make_i = "make_i"; export const TAGSTRING_CONTROL_index = "index"; export const TAGSTRING_CONTROL_index_address = "index-address"; export const TAGSTRING_CONTROL_constructor = "constructor"; diff --git a/src/go/heap/types/user/channel.ts b/src/go/heap/types/user/channel.ts index 1cd4aa9..19d7401 100644 --- a/src/go/heap/types/user/channel.ts +++ b/src/go/heap/types/user/channel.ts @@ -105,18 +105,34 @@ class UserChannel extends HeapObject { throw new Error("UserChannel.try_send: channel is closed"); } - if (this.buffer().length() < this.get_buffer_size()) { + while (this.waitingRecv().length() > 0) { + let waiting = this.waitingRecv().front() as ContextWaitingInstance; + if (waiting.get_waker().isEmpty()) { + waiting = this.waitingRecv().dequeue() as ContextWaitingInstance; + waiting.free(); + continue; + } else { + break; + } + } + + if ( + this.buffer().length() < this.get_buffer_size() || + this.waitingRecv().length() > 0 + ) { this.buffer().enqueue(value); - while (this.waitingRecv().length() > 0) { + if (this.waitingRecv().length() > 0) { const waiting = this.waitingRecv().dequeue() as ContextWaitingInstance; if (waiting.get_waker().isEmpty()) { - waiting.free(); - continue; + throw new Error( + "UserChannel.try_send: waiting receiver should have a waker" + ); } const new_value = this.buffer().dequeue(); // Guaranteed to be non-empty waiting.get_waker().get_thread().stash().push(new_value.address); + if (!waiting.get_body().is_nil()) { // For select case, push the case body to the thread's control stack waiting @@ -127,10 +143,8 @@ class UserChannel extends HeapObject { } waiting.get_waker().wake(scheduler); - new_value.free(); waiting.free(); - break; } return { success: true, waitingQueue: null }; @@ -185,36 +199,35 @@ class UserChannel extends HeapObject { if (this.get_tag() !== TAG_USER_channel) { throw new Error("UserChannel.get_buffer_size: invalid object tag"); } - if (this.buffer().length() > 0) { - const value = this.buffer().dequeue(); - thread.stash().push(value.address); - value.free(); - while (this.waitingSend().length() > 0) { - const waiting = this.waitingSend().dequeue() as ContextWaitingInstance; - if (waiting.get_waker().isEmpty()) { - waiting.free(); - continue; - } - - const new_value = waiting.get_value(); - this.buffer().enqueue(new_value); - if (!waiting.get_body().is_nil()) { - // For select case, push the case body to the thread's control stack - waiting - .get_waker() - .get_thread() - .control() - .push(waiting.get_body().address); - } + while (this.waitingSend().length() > 0) { + const waiting = this.waitingSend().dequeue() as ContextWaitingInstance; + if (waiting.get_waker().isEmpty()) { + waiting.free(); + continue; + } - waiting.get_waker().wake(scheduler); + const new_value = waiting.get_value(); + this.buffer().enqueue(new_value); - new_value.free(); - waiting.free(); - break; + if (!waiting.get_body().is_nil()) { + // For select case, push the case body to the thread's control stack + waiting + .get_waker() + .get_thread() + .control() + .push(waiting.get_body().address); } + waiting.get_waker().wake(scheduler); + waiting.free(); + break; + } + + if (this.buffer().length() > 0) { + const value = this.buffer().dequeue(); + thread.stash().push(value.address); + value.free(); return { success: true, waitingQueue: null }; } else { if (this.isClosed()) { @@ -272,7 +285,10 @@ class UserChannel extends HeapObject { public stringify_i(): string { let result = ""; result += this.address.toString() + " "; - + result += "channel "; + result += this.get_buffer_size().toString() + " "; + result += this.get_type().stringify() + " "; + result += this.isClosed() ? "closed" : "open"; return result; } }