Skip to content

Commit

Permalink
transientcontainers: add TransientQueue #30
Browse files Browse the repository at this point in the history
Add type and a 'TransientQueue' global function.
  • Loading branch information
GraphR00t committed Jan 3, 2024
1 parent 8105192 commit dffb08f
Show file tree
Hide file tree
Showing 10 changed files with 409 additions and 0 deletions.
5 changes: 5 additions & 0 deletions internal/globals/default_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/inoxlang/inox/internal/core"
"github.com/inoxlang/inox/internal/core/symbolic"
"github.com/inoxlang/inox/internal/globals/globalnames"
"github.com/inoxlang/inox/internal/globals/transientcontainers"
"github.com/inoxlang/inox/internal/globals/ws_ns"
"golang.org/x/exp/maps"

Expand Down Expand Up @@ -338,6 +339,10 @@ func NewDefaultGlobalState(ctx *core.Context, conf core.DefaultGlobalStateConfig
constants[k] = v
}

for k, v := range transientcontainers.NewTransientContainersNamespace() {
constants[k] = v
}

if conf.AbsoluteModulePath != "" {
constants[globalnames.MODULE_DIRPATH] = core.DirPathFrom(filepath.Dir(conf.AbsoluteModulePath))
constants[globalnames.MODULE_FILEPATH] = core.PathFrom(conf.AbsoluteModulePath)
Expand Down
28 changes: 28 additions & 0 deletions internal/globals/transientcontainers/namespace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package transientcontainers

import (
"github.com/inoxlang/inox/internal/core"
"github.com/inoxlang/inox/internal/core/symbolic"
transientsymb "github.com/inoxlang/inox/internal/globals/transientcontainers/symbolic"
"github.com/inoxlang/inox/internal/globals/transientcontainers/transientqueue"

"github.com/inoxlang/inox/internal/help"
)

var ()

func init() {
core.RegisterSymbolicGoFunctions([]any{
transientqueue.NewQueue, func(ctx *symbolic.Context, elements symbolic.Iterable) *transientsymb.TransientQueue {
return &transientsymb.TransientQueue{}
},
})

help.RegisterHelpValues(map[string]any{})
}

func NewTransientContainersNamespace() map[string]core.Value {
return map[string]core.Value{
"TransientQueue": core.ValOf(transientqueue.NewQueue),
}
}
53 changes: 53 additions & 0 deletions internal/globals/transientcontainers/symbolic/queue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package transientcontainers

import (
"github.com/inoxlang/inox/internal/core/symbolic"
)

var (
QUEUE_PROPNAMES = []string{"enqueue", "dequeue", "peek"}
_ = []symbolic.Iterable{(*TransientQueue)(nil)}
_ = []symbolic.PotentiallySharable{(*TransientQueue)(nil)}
)

type TransientQueue struct {
symbolic.UnassignablePropsMixin
shared bool
}

func (*TransientQueue) Test(v symbolic.Value, state symbolic.RecTestCallState) bool {
state.StartCall()
defer state.FinishCall()

_, ok := v.(*TransientQueue)
return ok
}

func (*TransientQueue) Enqueue(ctx *symbolic.Context, elem symbolic.Value) {
if ok, reason := symbolic.IsSharableOrClonable(elem); !ok {
if reason != "" {
reason = ": " + reason
}
ctx.AddSymbolicGoFunctionError("passed value is not sharable nor clonable" + reason)
}
}

func (*TransientQueue) Dequeue(ctx *symbolic.Context) (symbolic.Value, *symbolic.Bool) {
return symbolic.ANY, nil
}

func (*TransientQueue) Peek(ctx *symbolic.Context) (symbolic.Value, *symbolic.Bool) {
return symbolic.ANY, nil
}

func (*TransientQueue) IteratorElementKey() symbolic.Value {
return symbolic.ANY
}

func (*TransientQueue) IteratorElementValue() symbolic.Value {
return symbolic.ANY
}

func (*TransientQueue) WidestOfType() symbolic.Value {
return &TransientQueue{}
}
48 changes: 48 additions & 0 deletions internal/globals/transientcontainers/symbolic/queue_value_impl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package transientcontainers

import (
"github.com/inoxlang/inox/internal/core/symbolic"
"github.com/inoxlang/inox/internal/prettyprint"
)

func (q *TransientQueue) IsMutable() bool {
return true
}

func (q *TransientQueue) GetGoMethod(name string) (*symbolic.GoFunction, bool) {
switch name {
case "enqueue":
return symbolic.WrapGoMethod(q.Enqueue), true
case "dequeue":
return symbolic.WrapGoMethod(q.Dequeue), true
case "peek":
return symbolic.WrapGoMethod(q.Peek), true
}
return nil, false
}

func (q *TransientQueue) Prop(name string) symbolic.Value {
return symbolic.GetGoMethodOrPanic(name, q)
}

func (*TransientQueue) PropertyNames() []string {
return QUEUE_PROPNAMES
}

func (*TransientQueue) PrettyPrint(w prettyprint.PrettyPrintWriter, config *prettyprint.PrettyPrintConfig) {
w.WriteName("transient-queue")
}

func (q *TransientQueue) IsSharable() (bool, string) {
return true, ""
}

func (q *TransientQueue) Share(originState *symbolic.State) symbolic.PotentiallySharable {
copy := *q
copy.shared = true
return &copy
}

func (q *TransientQueue) IsShared() bool {
return q.shared
}
39 changes: 39 additions & 0 deletions internal/globals/transientcontainers/transientqueue/iterator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package transientqueue

import (
"github.com/inoxlang/inox/internal/core"
"github.com/inoxlang/inox/internal/globals/containers/common"
"github.com/inoxlang/inox/internal/memds"
)

func (s *TransientQueue) Iterator(ctx *core.Context, config core.IteratorConfiguration) core.Iterator {
var it *memds.ArrayQueueIterator[core.Value]
if s.threadUnsafe != nil {
it = s.threadUnsafe.Iterator()
} else {
it = s.threadSafe.Iterator()
}
var next core.Value

return config.CreateIterator(&common.CollectionIterator{
HasNext_: func(ci *common.CollectionIterator, ctx *core.Context) bool {
if next == nil {
if !it.Next() {
return false
}
next = it.Value()
}
return true
},
Next_: func(ci *common.CollectionIterator, ctx *core.Context) bool {
next = nil
return true
},
Key_: func(ci *common.CollectionIterator, ctx *core.Context) core.Value {
return core.Int(it.Index())
},
Value_: func(ci *common.CollectionIterator, ctx *core.Context) core.Value {
return it.Value()
},
})
}
38 changes: 38 additions & 0 deletions internal/globals/transientcontainers/transientqueue/share.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package transientqueue

import (
"github.com/inoxlang/inox/internal/core"
"github.com/inoxlang/inox/internal/memds"
)

// PotentiallySharable impl for TransientQueue

func (*TransientQueue) IsSharable(originState *core.GlobalState) (bool, string) {
return true, ""
}

func (q *TransientQueue) Share(originState *core.GlobalState) {
threadSafeQueue := memds.NewTSArrayQueue[core.Value]()

q.threadUnsafe.ForEachElem(func(i int, e core.Value) error {
shared, err := core.ShareOrClone(e, originState)
if err != nil {
panic(err)
}
threadSafeQueue.Enqueue(shared)
return nil
})

q.threadUnsafe = nil
q.threadSafe = threadSafeQueue
}

func (q *TransientQueue) IsShared() bool {
return q.threadSafe != nil
}

func (*TransientQueue) ForceLock() {
}

func (*TransientQueue) ForceUnlock() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package transientqueue

import (
"github.com/inoxlang/inox/internal/core"
"github.com/inoxlang/inox/internal/memds"
)

var _ = core.PotentiallySharable((*TransientQueue)(nil))

func NewQueue(ctx *core.Context, elements core.Iterable) *TransientQueue {
queue := &TransientQueue{
threadUnsafe: memds.NewArrayQueue[core.Value](),
}

it := elements.Iterator(ctx, core.IteratorConfiguration{})
for it.Next(ctx) {
e := it.Value(ctx)
queue.Enqueue(ctx, e)
}

return queue
}

type TransientQueue struct {
threadUnsafe *memds.ArrayQueue[core.Value] //set to nil when shared
threadSafe *memds.TSArrayQueue[core.Value] //set if shared
}

func (q *TransientQueue) Enqueue(ctx *core.Context, elem core.Value) {
if q.threadUnsafe != nil {
q.threadUnsafe.Enqueue(elem)
return
}
//thread safe
elem, err := core.ShareOrClone(elem, ctx.GetClosestState())
if err != nil {
panic(err)
}
q.threadSafe.Enqueue(elem)
}

func (q *TransientQueue) Dequeue(ctx *core.Context) (core.Value, core.Bool) {
if q.threadUnsafe != nil {
e, ok := q.threadUnsafe.Dequeue()
return e, core.Bool(ok)
}
e, ok := q.threadSafe.Dequeue()
return e, core.Bool(ok)
}

func (q *TransientQueue) Peek(ctx *core.Context) (core.Value, core.Bool) {
if q.threadUnsafe != nil {
e, ok := q.threadUnsafe.Peek()
return e, core.Bool(ok)
}
e, ok := q.threadSafe.Peek()
return e, core.Bool(ok)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package transientqueue

import (
"testing"

"github.com/inoxlang/inox/internal/core"
"github.com/stretchr/testify/assert"
)

func TestTransientQueue(t *testing.T) {
ctx := core.NewContexWithEmptyState(core.ContextConfig{}, nil)
defer ctx.CancelGracefully()

q := NewQueue(ctx, core.NewWrappedValueList())

//Add a sharable1 element and a clonable element.
sharable1 := core.NewObjectFromMapNoInit(core.ValMap{})
clonable1 := core.NewWrappedValueList(core.Int(1))

q.Enqueue(ctx, sharable1)
q.Enqueue(ctx, clonable1)

//Elements added to an unshared queue should not be shared.
if !assert.False(t, sharable1.IsShared()) {
return
}

//Sharing the queue should cause the elements to be shared or cloned.
q.Share(ctx.GetClosestState())

if !assert.True(t, sharable1.IsShared()) {
return
}

//Shared queues should or clone added elements.
sharable2 := core.NewObjectFromMapNoInit(core.ValMap{})
clonable2 := core.NewWrappedValueList(core.Int(2))

q.Enqueue(ctx, sharable2)
q.Enqueue(ctx, clonable2)

//Dequeue and check elements.
first, ok := q.Dequeue(ctx)
if !assert.True(t, bool(ok)) {
return
}

assert.Same(t, sharable1, first)

second, ok := q.Dequeue(ctx)
if !assert.True(t, bool(ok)) {
return
}

assert.NotSame(t, clonable1, second)
assert.True(t, clonable1.Equal(ctx, second, map[uintptr]uintptr{}, 0))

third, ok := q.Dequeue(ctx)
if !assert.True(t, bool(ok)) {
return
}

assert.Same(t, sharable2, third)

fourth, ok := q.Dequeue(ctx)
if !assert.True(t, bool(ok)) {
return
}

assert.NotSame(t, clonable2, fourth)
assert.True(t, clonable2.Equal(ctx, fourth, map[uintptr]uintptr{}, 0))
}
Loading

0 comments on commit dffb08f

Please sign in to comment.