Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
AmrSaber committed May 31, 2022
1 parent bd74005 commit e5f7150
Showing 1 changed file with 179 additions and 2 deletions.
181 changes: 179 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,187 @@
# Blocking Dequeue

![Tests status](https://github.com/AmrSaber/go-blocking-dequeue/actions/workflows/tests.yaml/badge.svg?branch=master)
![GitHub release downloads (latest by SemVer)](https://img.shields.io/github/downloads/AmrSaber/go-blocking-dequeue/latest/total?sort=semver)
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/AmrSaber/go-blocking-dequeue?sort=semver)
![GitHub license](https://img.shields.io/github/license/AmrSaber/go-blocking-dequeue)

This package (repo) provides an implementation of a thread-safe, blocking, generic, infinite dequeue that can be used as FIFO or LIFO or a hybrid between the 2.

TODO: Add (importing, usage (mention how to initialize) with examples, add go documentation link, limitations and drawbacks)
## Installation

To install this package, you will need to setup your go workspace first. Also this package requires go **v1.18** or later.

1. To install the package run the following command:

```bash
go get github.com/AmrSaber/go-blocking-dequeue
```

2. To import the package:

```go
import "github.com/AmrSaber/go-blocking-dequeue"
```

3. Use the package in code using `blocking_dequeue` module (see usage below).

## Usage

### Initialization

To create a new dequeue use `blocking_dequeue.NewBlockingDequeue` function as follows:

```go
// Integers dequeue
integersDequeue := blocking_dequeue.NewBlockingDequeue[int]()
integersDequeue.PushBack(10)

// Strings dequeue
stringsDequeue := blocking_dequeue.NewBlockingDequeue[string]()
stringsDequeue.PushBack("hello")

type User struct {
Username string
Age int
}

// Dequeue of custom type
usersDequeue := blocking_dequeue.NewBlockingDequeue[User]()
usersDequeue.PushBack(User{ "Amr", 25 })

// Pointer dequeue
usersPtrDequeue := blocking_dequeue.NewBlockingDequeue[*User]()
usersPtrDequeue.PushBack(&User{ "Amr", 25 })
```

The dequeue is implemented using generics, so it can hold any datatype, just pass the data type you want between the square brackets `[ ]`.

### Capacity

You can set a capacity if you want (the default is unlimited capacity)

```go
dequeue.SetCapacity(100) // Limit the dequeue to only carry 100 elements
dequeue.SetCapacity(0) // Infinite capacity (the default)
```

Some notes about setting the capacity:

- Pushing to the dequeue will block if it has full capacity, the goroutine will be blocked until some values are popped from the dequeue.
- As in the above example, setting the capacity to 0 means having infinite capacity.
- If you attempt the set the capacity to be lower than the current size of the dequeue, the method `SetCapacity` will return an error, and the capacity won't be updated. The same thing will also happen if you try to set the capacity with a negative number.
- You can always set the capacity to 0 regardless of the current size.

### Usage as Queue

```go
dq := blocking_dequeue.NewBlockingDequeue[int]()

dq.PushBack(1) // Pushed to the end of the dequeue
dq.PushBack(2) // Pushed to the end of the dequeue
dq.PushBack(3) // Pushed to the end of the dequeue

dq.PopFront() // Pops from the top, returns 1
dq.PopFront() // Pops from the top, returns 2
dq.PopFront() // Pops from the top, returns 3
```

### Usage as Stack

```go
dq := blocking_dequeue.NewBlockingDequeue[int]()

dq.PushFront(1) // Pushed to the start of the dequeue
dq.PushFront(2) // Pushed to the start of the dequeue
dq.PushFront(3) // Pushed to the start of the dequeue

dq.PopFront() // Pops from the top, returns 3
dq.PopFront() // Pops from the top, returns 2
dq.PopFront() // Pops from the top, returns 1
```

### Listeners

You can attach the listeners `OnEmpty` and `OnFull` to be invoked when the dequeue is empty or full respectively

```go
dq := blocking_dequeue.NewBlockingDequeue[int]()

dq.IsEmpty = func() {
fmt.Println("Dequeue is now empty")
}

dq.PushBack(1)
dq.PopFront() // "Dequeue is now empty" is printed as IsEmpty listener is invoked

// Dequeue can only be full if it has a positive capacity
dq.SetCapacity(2)

dq.IsFull = func() {
fmt.Println("Dequeue is now full")
}

dq.PushBack(1)
dq.PushBack(2) // "Dequeue is now full" is printed as IsFull listener is invoked
```

## API Documentation

The package itself exposes 1 function `NewBlockingQueue` that is used to create a new dequeue and return a pointer to it.

The dequeue itself exposes the following methods:

- `PushFront`, `PushBack`
- `PopFront`, `PopBack`
- `PeekFront`, `PeekBack`
- `Capacity`, `SetCapacity`, `IsFull`
- `Size`, `IsEmpty`

It also supports the listeners (see example above):

- `IsEmpty`
- `IsFull`

The detailed documentation can be found at the related [go packages page](https://pkg.go.dev/github.com/AmrSaber/go-blocking-dequeue).

## Limitations and Drawbacks

This dequeue is implemented using build-in `container/list` so all of the operations are done in O(1) time complexity.

However, due to the thread-safe nature, and all the lock/unlock/wait/signal/broadcast logic, it's expected to be a bit slower than the plain `container/list`. If you intend to use this dequeue in a single threaded context (where only a single goroutine will have access to it) it's advised to use the built-in `container/list` instead.

If you intend to use it as a limited capacity queue to communicate between goroutines, it would be better to use built-in channels with buffer, so instead of

```go
dq := blocking_dequeue.NewBlockingDequeue[int]()
dq.SetCapacity(10)

// Push to queue
dq.PushBack(1)

// Pop from queue
dq.PopFront()
```

You better use

```go
ch := make(chan int, 10)

// Push to queue
ch <- 1

// Pop from queue
<- ch
```

That is unless you need access the other provided methods and listeners, such as `Peek` variations, `Size`, `IsFull`, `OnEmpty`, `OnFull`, and so on...

## Benchmarking

No benchmarking against the built-in `container/list` nor channels yet. But it's in the plan.

## Contribution

If you find a bug, you are welcome to [create a ticket on github](https://github.com/AmrSaber/go-blocking-dequeue/issues) or create a PR with the fix directly mentioning in the description of the PR what is the problem and how your PR fixes it.

If you want to suggest a feature (even if you have no idea how it will be implemented) feel free to [open a ticket with it](https://github.com/AmrSaber/go-blocking-dequeue/issues).

0 comments on commit e5f7150

Please sign in to comment.