Skip to content

Commit

Permalink
feat: mutex (#33)
Browse files Browse the repository at this point in the history
* feat: add grammar for mutexes

* feat: add pointer tag for mutexes

* feat: add types for mutex ops and edit new to accommodate mutexes

* feat: implement Mutex data structure

* feat: implement Mutex Add and Lock

* chore: format files

* fix: undefined action bug
  • Loading branch information
alvynben authored Apr 15, 2024
1 parent 0d1a5dd commit c39dedd
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 154 deletions.
34 changes: 34 additions & 0 deletions src/go-slang/goroutine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import { Scheduler } from './scheduler'
import { PredeclaredFuncT } from './lib/predeclared'
import { BufferedChannel, UnbufferedChannel } from './lib/channel'
import { WaitGroup } from './lib/waitgroup'
import { Mutex } from './lib/mutex'

export type Control = Stack<Instruction | HeapAddress>
export type Stash = Stack<HeapAddress | any>
Expand Down Expand Up @@ -332,6 +333,22 @@ const Interpreter: {
return C.push(action)
}

if (className instanceof Mutex) {
const methodActions = {
Lock: () => ({ type: CommandType.MutexLockOp }),
Unlock: () => ({ type: CommandType.MutexUnlockOp })
}

const action = methodActions[callee.method.name]?.()
if (!action) {
return Result.fail(new UndefinedError(callee.method.name, callee.method.loc!))
}

const mutexHeapAddress = E.lookup(callee.pkg.name)
S.push(mutexHeapAddress)
return C.push(action)
}

// Should be unreachable
return Result.fail(new UndefinedError(callee.method.name, callee.method.loc!))
},
Expand Down Expand Up @@ -502,6 +519,23 @@ const Interpreter: {
return void S.pop()
},

MutexLockOp: (_inst, { C, S, H }) => {
const mutex = H.resolve(S.peek()) as Mutex
if (mutex.isLocked()) {
C.push(_inst)
return Result.ok(GoRoutineState.Blocked)
}

mutex.lock()
return void S.pop()
},

MutexUnlockOp: (_inst, { S, H }) => {
const mutex = H.resolve(S.peek()) as Mutex
mutex.unlock()
return void S.pop()
},

BranchOp: ({ cons, alt }: BranchOp, { S, C, H }) =>
void (H.resolve(S.pop()) ? C.pushR(H.alloc(cons)) : alt && C.pushR(H.alloc(alt))),

Expand Down
13 changes: 13 additions & 0 deletions src/go-slang/lib/heap/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from '../../types'
import { AstMap } from '../astMap'
import { BufferedChannel, UnbufferedChannel } from '../channel'
import { Mutex } from '../mutex'
import { WaitGroup } from '../waitgroup'
import { DEFAULT_HEAP_SIZE, SIZE_OFFSET, WORD_SIZE } from './config'
import { PointerTag } from './tags'
Expand Down Expand Up @@ -173,6 +174,8 @@ export class Heap {
switch (value.type) {
case NewType.WaitGroup:
return this.allocateWaitGroup()
case NewType.Mutex:
return this.allocateMutex()
}
}

Expand Down Expand Up @@ -261,6 +264,8 @@ export class Heap {
return new BufferedChannel(chanMemRegion)
case PointerTag.WaitGroup:
return new WaitGroup(new DataView(this.memory.buffer, heap_addr, WORD_SIZE))
case PointerTag.Mutex:
return new Mutex(new DataView(this.memory.buffer, heap_addr, WORD_SIZE))
}
}

Expand Down Expand Up @@ -382,6 +387,14 @@ export class Heap {
return ptr_heap_addr
}

/* Memory Layout of a Mutex: [0:tag, 1-6:_unused, 7:isLocked] (1 word) */
public allocateMutex(): HeapAddress {
const ptr_heap_addr = this.allocateTaggedPtr(PointerTag.Mutex)
this.memory.setUint8(ptr_heap_addr + 7, 0) // initialize isLocked to false

return ptr_heap_addr
}

/**
* Allocate a tagged pointer in the heap
*
Expand Down
3 changes: 2 additions & 1 deletion src/go-slang/lib/heap/tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export enum PointerTag {
PopSOp,
BufferedChannel,
UnbufferedChannel,
WaitGroup
WaitGroup,
Mutex
}
32 changes: 32 additions & 0 deletions src/go-slang/lib/mutex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export class Mutex {
protected memory: DataView
protected LOCKED_OFFSET = 7

toString(): string {
return `Mutex { isLocked: ${this.getisLocked()} }`
}

constructor(memory: DataView) {
this.memory = memory
}

protected getisLocked(): boolean {
return this.memory.getUint8(this.LOCKED_OFFSET) === 1
}

protected setisLocked(value: boolean): void {
this.memory.setUint8(this.LOCKED_OFFSET, value ? 1 : 0)
}

public isLocked(): boolean {
return this.getisLocked()
}

public lock(): void {
this.setisLocked(true)
}

public unlock(): void {
this.setisLocked(false)
}
}
14 changes: 12 additions & 2 deletions src/go-slang/lib/predeclared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export interface PredeclaredFunc {
export const PREDECLARED_IDENTIFIERS: { [key: string]: any } = {
true: true,
false: false,
'sync.WaitGroup': NewType.WaitGroup
'sync.WaitGroup': NewType.WaitGroup,
'sync.Mutex': NewType.Mutex
}

/**
Expand Down Expand Up @@ -91,12 +92,21 @@ function _new(...args: any): New | InvalidOperationError {
if (type.value === NewType.WaitGroup) {
if (args.length > 1) {
return new InvalidOperationError(
`new(${NewType.WaitGroup}, ${args.slice(1).join(', ')}) expects 1 or 2 arguments; found ${args.length}`
`new(${NewType.WaitGroup}, ${args.slice(1).join(', ')}) expects 1 argument; found ${args.length}`
)
}
return { type: NewType.WaitGroup, count: 0 } as NewWaitGroup
}

if (type.value === NewType.Mutex) {
if (args.length > 1) {
return new InvalidOperationError(
`new(${NewType.Mutex}, ${args.slice(1).join(', ')}) expects 1 arguments; found ${args.length}`
)
}
return { type: NewType.Mutex } as New
}

// NOTE: this should be unreachable
return new InvalidOperationError(`new: cannot make type ${type.value}`)
}
Expand Down
352 changes: 204 additions & 148 deletions src/go-slang/parser/go.js

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions src/go-slang/parser/go.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ StringLit
}

TypeLit
= ( WaitGroupType / ChannelType ) { return makeNode({ type: "TypeLiteral", value: text() }) }
= ( WaitGroupType / ChannelType / MutexType ) { return makeNode({ type: "TypeLiteral", value: text() }) }

MutexType
= SYNCMUTEX_TOKEN

WaitGroupType
= SYNCWAITGROUP_TOKEN
Expand Down Expand Up @@ -463,9 +466,11 @@ IMPORT_TOKEN = "import" !IdentifierPart
RETURN_TOKEN = "return" !IdentifierPart
VAR_TOKEN = "var" !IdentifierPart
SYNCWAITGROUP_TOKEN = "sync.WaitGroup" !IdentifierPart
SYNCMUTEX_TOKEN = "sync.Mutex" !IdentifierPart

Keyword
= SYNCWAITGROUP_TOKEN
= SYNCMUTEX_TOKEN
/ SYNCWAITGROUP_TOKEN
/ BREAK_TOKEN
/ DEFAULT_TOKEN
/ FUNC_TOKEN
Expand Down
9 changes: 8 additions & 1 deletion src/go-slang/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ export interface FunctionLiteral extends Node {
}

export enum NewType {
WaitGroup = 'sync.WaitGroup'
WaitGroup = 'sync.WaitGroup',
Mutex = 'sync.Mutex'
}

export enum MakeType {
Expand Down Expand Up @@ -271,6 +272,8 @@ export enum CommandType {
WaitGroupAddOp = 'WaitGroupAddOp',
WaitGroupDoneOp = 'WaitGroupDoneOp',
WaitGroupWaitOp = 'WaitGroupWaitOp',
MutexLockOp = 'MutexLockOp',
MutexUnlockOp = 'MutexUnlockOp',
BranchOp = 'BranchOp',
EnvOp = 'EnvOp',
PopSOp = 'PopSOp',
Expand Down Expand Up @@ -469,3 +472,7 @@ export interface NewWaitGroup extends New {
type: NewType.WaitGroup
count: number
}

export interface NewMutex extends New {
type: NewType.Mutex
}

0 comments on commit c39dedd

Please sign in to comment.