mirror of
https://github.com/safing/portbase
synced 2025-09-01 18:19:57 +00:00
Remove config and use service workers for goroutines
This commit is contained in:
parent
c58d6a0f30
commit
30a6948009
7 changed files with 125 additions and 179 deletions
|
@ -1,17 +1,20 @@
|
||||||
package rng
|
package rng
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
"github.com/tevino/abool"
|
"github.com/tevino/abool"
|
||||||
|
|
||||||
"github.com/safing/portbase/config"
|
|
||||||
"github.com/safing/portbase/container"
|
"github.com/safing/portbase/container"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
minFeedEntropy = 256
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
rngFeeder = make(chan []byte)
|
rngFeeder = make(chan []byte)
|
||||||
minFeedEntropy config.IntOption
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// The Feeder is used to feed entropy to the RNG.
|
// The Feeder is used to feed entropy to the RNG.
|
||||||
|
@ -34,7 +37,7 @@ func NewFeeder() *Feeder {
|
||||||
needsEntropy: abool.NewBool(true),
|
needsEntropy: abool.NewBool(true),
|
||||||
buffer: container.New(),
|
buffer: container.New(),
|
||||||
}
|
}
|
||||||
go new.run()
|
module.StartServiceWorker("feeder", 0, new.run)
|
||||||
return new
|
return new
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +90,7 @@ func (f *Feeder) CloseFeeder() {
|
||||||
f.input <- nil
|
f.input <- nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Feeder) run() {
|
func (f *Feeder) run(ctx context.Context) error {
|
||||||
defer f.needsEntropy.UnSet()
|
defer f.needsEntropy.UnSet()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -97,23 +100,26 @@ func (f *Feeder) run() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case newEntropy := <-f.input:
|
case newEntropy := <-f.input:
|
||||||
if newEntropy != nil {
|
// check if feed has been closed
|
||||||
f.buffer.Append(newEntropy.data)
|
if newEntropy == nil {
|
||||||
f.entropy += int64(newEntropy.entropy)
|
return nil
|
||||||
if f.entropy >= minFeedEntropy() {
|
|
||||||
break gather
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case <-shutdownSignal:
|
// append to buffer
|
||||||
return
|
f.buffer.Append(newEntropy.data)
|
||||||
|
f.entropy += int64(newEntropy.entropy)
|
||||||
|
if f.entropy >= minFeedEntropy {
|
||||||
|
break gather
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// feed
|
// feed
|
||||||
f.needsEntropy.UnSet()
|
f.needsEntropy.UnSet()
|
||||||
select {
|
select {
|
||||||
case rngFeeder <- f.buffer.CompileData():
|
case rngFeeder <- f.buffer.CompileData():
|
||||||
case <-shutdownSignal:
|
case <-ctx.Done():
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
f.buffer = container.New()
|
f.buffer = container.New()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,27 @@
|
||||||
package rng
|
package rng
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
fullFeedDuration = 100 * time.Millisecond
|
|
||||||
)
|
|
||||||
|
|
||||||
func getFullFeedDuration() time.Duration {
|
func getFullFeedDuration() time.Duration {
|
||||||
|
|
||||||
// full feed every 5x time of reseedAfterSeconds
|
// full feed every 5x time of reseedAfterSeconds
|
||||||
secsUntilFullFeed := reseedAfterSeconds() * 5
|
secsUntilFullFeed := reseedAfterSeconds * 5
|
||||||
|
|
||||||
// full feed at most once per minute
|
// full feed at most once every ten minutes
|
||||||
if secsUntilFullFeed < 60 {
|
if secsUntilFullFeed < 600 {
|
||||||
secsUntilFullFeed = 60
|
secsUntilFullFeed = 600
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Duration(secsUntilFullFeed * int64(time.Second))
|
return time.Duration(secsUntilFullFeed) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
func fullFeeder() {
|
func fullFeeder(ctx context.Context) error {
|
||||||
for {
|
fullFeedDuration := getFullFeedDuration()
|
||||||
|
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
case <-time.After(fullFeedDuration):
|
case <-time.After(fullFeedDuration):
|
||||||
|
|
||||||
|
@ -39,11 +37,8 @@ func fullFeeder() {
|
||||||
}
|
}
|
||||||
rngLock.Unlock()
|
rngLock.Unlock()
|
||||||
|
|
||||||
case <-shutdownSignal:
|
case <-ctx.Done():
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fullFeedDuration = getFullFeedDuration()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
rng/get.go
14
rng/get.go
|
@ -6,19 +6,19 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"time"
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
"github.com/safing/portbase/config"
|
const (
|
||||||
|
reseedAfterSeconds = 600 // ten minutes
|
||||||
|
reseedAfterBytes = 1048576 // one megabyte
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Reader provides a global instance to read from the RNG.
|
// Reader provides a global instance to read from the RNG.
|
||||||
Reader io.Reader
|
Reader io.Reader
|
||||||
|
|
||||||
rngBytesRead int64
|
rngBytesRead uint64
|
||||||
rngLastFeed = time.Now()
|
rngLastFeed = time.Now()
|
||||||
|
|
||||||
reseedAfterSeconds config.IntOption
|
|
||||||
reseedAfterBytes config.IntOption
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// reader provides an io.Reader interface
|
// reader provides an io.Reader interface
|
||||||
|
@ -32,8 +32,8 @@ func checkEntropy() (err error) {
|
||||||
if !rngReady {
|
if !rngReady {
|
||||||
return errors.New("RNG is not ready yet")
|
return errors.New("RNG is not ready yet")
|
||||||
}
|
}
|
||||||
if rngBytesRead > reseedAfterBytes() ||
|
if rngBytesRead > reseedAfterBytes ||
|
||||||
int64(time.Since(rngLastFeed).Seconds()) > reseedAfterSeconds() {
|
int(time.Since(rngLastFeed).Seconds()) > reseedAfterSeconds {
|
||||||
select {
|
select {
|
||||||
case r := <-rngFeeder:
|
case r := <-rngFeeder:
|
||||||
rng.Reseed(r)
|
rng.Reseed(r)
|
||||||
|
|
|
@ -1,35 +1,36 @@
|
||||||
package rng
|
package rng
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"time"
|
"fmt"
|
||||||
|
|
||||||
"github.com/safing/portbase/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func osFeeder() {
|
func osFeeder(ctx context.Context) error {
|
||||||
|
|
||||||
|
entropyBytes := minFeedEntropy / 8
|
||||||
feeder := NewFeeder()
|
feeder := NewFeeder()
|
||||||
|
defer feeder.CloseFeeder()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
// gather
|
||||||
// get feed entropy
|
osEntropy := make([]byte, entropyBytes)
|
||||||
minEntropyBytes := int(minFeedEntropy())/8 + 1
|
|
||||||
if minEntropyBytes < 32 {
|
|
||||||
minEntropyBytes = 64
|
|
||||||
}
|
|
||||||
|
|
||||||
// get entropy
|
|
||||||
osEntropy := make([]byte, minEntropyBytes)
|
|
||||||
n, err := rand.Read(osEntropy)
|
n, err := rand.Read(osEntropy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("could not read entropy from os: %s", err)
|
return fmt.Errorf("could not read entropy from os: %s", err)
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
}
|
}
|
||||||
if n != minEntropyBytes {
|
if n != entropyBytes {
|
||||||
log.Errorf("could not read enough entropy from os: got only %d bytes instead of %d", n, minEntropyBytes)
|
return fmt.Errorf("could not read enough entropy from os: got only %d bytes instead of %d", n, entropyBytes)
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// feed
|
// feed
|
||||||
feeder.SupplyEntropy(osEntropy, minEntropyBytes*8)
|
select {
|
||||||
|
case feeder.input <- &entropyData{
|
||||||
|
data: osEntropy,
|
||||||
|
entropy: entropyBytes * 8,
|
||||||
|
}:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
98
rng/rng.go
98
rng/rng.go
|
@ -3,122 +3,60 @@ package rng
|
||||||
import (
|
import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/aead/serpent"
|
"github.com/aead/serpent"
|
||||||
"github.com/seehuhn/fortuna"
|
"github.com/seehuhn/fortuna"
|
||||||
|
|
||||||
"github.com/safing/portbase/config"
|
|
||||||
"github.com/safing/portbase/modules"
|
"github.com/safing/portbase/modules"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
rng *fortuna.Generator
|
rng *fortuna.Generator
|
||||||
rngLock sync.Mutex
|
rngLock sync.Mutex
|
||||||
rngReady = false
|
rngReady = false
|
||||||
rngCipherOption config.StringOption
|
|
||||||
|
|
||||||
shutdownSignal = make(chan struct{})
|
rngCipher = "aes"
|
||||||
|
// possible values: aes, serpent
|
||||||
|
|
||||||
|
module *modules.Module
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
modules.Register("random", prep, Start, nil)
|
module = modules.Register("random", nil, start, nil)
|
||||||
}
|
|
||||||
|
|
||||||
func prep() error {
|
|
||||||
err := config.Register(&config.Option{
|
|
||||||
Name: "RNG Cipher",
|
|
||||||
Key: "random/rng_cipher",
|
|
||||||
Description: "Cipher to use for the Fortuna RNG. Requires restart to take effect.",
|
|
||||||
OptType: config.OptTypeString,
|
|
||||||
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
|
||||||
ReleaseLevel: config.ReleaseLevelExperimental,
|
|
||||||
ExternalOptType: "string list",
|
|
||||||
DefaultValue: "aes",
|
|
||||||
ValidationRegex: "^(aes|serpent)$",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rngCipherOption = config.GetAsString("random/rng_cipher", "aes")
|
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
|
||||||
Name: "Minimum Feed Entropy",
|
|
||||||
Key: "random/min_feed_entropy",
|
|
||||||
Description: "The minimum amount of entropy before a entropy source is feed to the RNG, in bits.",
|
|
||||||
OptType: config.OptTypeInt,
|
|
||||||
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
|
||||||
ReleaseLevel: config.ReleaseLevelExperimental,
|
|
||||||
DefaultValue: 256,
|
|
||||||
ValidationRegex: "^[0-9]{3,5}$",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
minFeedEntropy = config.Concurrent.GetAsInt("random/min_feed_entropy", 256)
|
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
|
||||||
Name: "Reseed after x seconds",
|
|
||||||
Key: "random/reseed_after_seconds",
|
|
||||||
Description: "Number of seconds until reseed",
|
|
||||||
OptType: config.OptTypeInt,
|
|
||||||
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
|
||||||
ReleaseLevel: config.ReleaseLevelExperimental,
|
|
||||||
DefaultValue: 360, // ten minutes
|
|
||||||
ValidationRegex: "^[1-9][0-9]{1,5}$",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
reseedAfterSeconds = config.Concurrent.GetAsInt("random/reseed_after_seconds", 360)
|
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
|
||||||
Name: "Reseed after x bytes",
|
|
||||||
Key: "random/reseed_after_bytes",
|
|
||||||
Description: "Number of fetched bytes until reseed",
|
|
||||||
OptType: config.OptTypeInt,
|
|
||||||
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
|
||||||
ReleaseLevel: config.ReleaseLevelExperimental,
|
|
||||||
DefaultValue: 1000000, // one megabyte
|
|
||||||
ValidationRegex: "^[1-9][0-9]{2,9}$",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
reseedAfterBytes = config.GetAsInt("random/reseed_after_bytes", 1000000)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCipher(key []byte) (cipher.Block, error) {
|
func newCipher(key []byte) (cipher.Block, error) {
|
||||||
cipher := rngCipherOption()
|
switch rngCipher {
|
||||||
switch cipher {
|
|
||||||
case "aes":
|
case "aes":
|
||||||
return aes.NewCipher(key)
|
return aes.NewCipher(key)
|
||||||
case "serpent":
|
case "serpent":
|
||||||
return serpent.NewCipher(key)
|
return serpent.NewCipher(key)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown or unsupported cipher: %s", cipher)
|
return nil, fmt.Errorf("unknown or unsupported cipher: %s", rngCipher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the RNG. Normally, this should be only called by the portbase/modules package.
|
func start() error {
|
||||||
func Start() (err error) {
|
|
||||||
rngLock.Lock()
|
rngLock.Lock()
|
||||||
defer rngLock.Unlock()
|
defer rngLock.Unlock()
|
||||||
|
|
||||||
rng = fortuna.NewGenerator(newCipher)
|
rng = fortuna.NewGenerator(newCipher)
|
||||||
|
if rng == nil {
|
||||||
|
return errors.New("failed to initialize rng")
|
||||||
|
}
|
||||||
rngReady = true
|
rngReady = true
|
||||||
|
|
||||||
// random source: OS
|
// random source: OS
|
||||||
go osFeeder()
|
module.StartServiceWorker("os rng feeder", 0, osFeeder)
|
||||||
|
|
||||||
// random source: goroutine ticks
|
// random source: goroutine ticks
|
||||||
go tickFeeder()
|
module.StartServiceWorker("tick rng feeder", 0, tickFeeder)
|
||||||
|
|
||||||
// full feeder
|
// full feeder
|
||||||
go fullFeeder()
|
module.StartServiceWorker("full feeder", 0, fullFeeder)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,17 +2,10 @@ package rng
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/safing/portbase/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
err := prep()
|
err := start()
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = Start()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -21,25 +14,17 @@ func init() {
|
||||||
func TestRNG(t *testing.T) {
|
func TestRNG(t *testing.T) {
|
||||||
key := make([]byte, 16)
|
key := make([]byte, 16)
|
||||||
|
|
||||||
err := config.SetConfigOption("random/rng_cipher", "aes")
|
rngCipher = "aes"
|
||||||
if err != nil {
|
_, err := newCipher(key)
|
||||||
t.Errorf("failed to set random/rng_cipher config: %s", err)
|
|
||||||
}
|
|
||||||
_, err = newCipher(key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to create aes cipher: %s", err)
|
t.Errorf("failed to create aes cipher: %s", err)
|
||||||
}
|
}
|
||||||
rng.Reseed(key)
|
|
||||||
|
|
||||||
err = config.SetConfigOption("random/rng_cipher", "serpent")
|
rngCipher = "serpent"
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to set random/rng_cipher config: %s", err)
|
|
||||||
}
|
|
||||||
_, err = newCipher(key)
|
_, err = newCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to create serpent cipher: %s", err)
|
t.Errorf("failed to create serpent cipher: %s", err)
|
||||||
}
|
}
|
||||||
rng.Reseed(key)
|
|
||||||
|
|
||||||
b := make([]byte, 32)
|
b := make([]byte, 32)
|
||||||
_, err = Read(b)
|
_, err = Read(b)
|
||||||
|
@ -55,4 +40,9 @@ func TestRNG(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Bytes failed: %s", err)
|
t.Errorf("Bytes failed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = Number(100)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Number failed: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,25 @@
|
||||||
package rng
|
package rng
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func getTickFeederTickDuration() time.Duration {
|
||||||
tickDuration = 1 * time.Millisecond
|
|
||||||
)
|
|
||||||
|
|
||||||
func getTickDuration() time.Duration {
|
|
||||||
|
|
||||||
// be ready in 1/10 time of reseedAfterSeconds
|
// be ready in 1/10 time of reseedAfterSeconds
|
||||||
msecsAvailable := reseedAfterSeconds() * 100
|
msecsAvailable := reseedAfterSeconds * 100
|
||||||
// ex.: reseed after 10 minutes: msecsAvailable = 36000
|
// ex.: reseed after 10 minutes: msecsAvailable = 60000
|
||||||
// have full entropy after 5 minutes
|
// have full entropy after 5 minutes
|
||||||
|
|
||||||
// one tick generates 0,125 bits of entropy
|
// one tick generates 0,125 bits of entropy
|
||||||
ticksNeeded := minFeedEntropy() * 8
|
ticksNeeded := minFeedEntropy * 8
|
||||||
// ex.: minimum entropy is 256: ticksNeeded = 2048
|
// ex.: minimum entropy is 256: ticksNeeded = 2048
|
||||||
|
|
||||||
// msces between ticks
|
// msces between ticks
|
||||||
tickMsecs := msecsAvailable / ticksNeeded
|
tickMsecs := msecsAvailable / ticksNeeded
|
||||||
// ex.: tickMsecs = 17(,578125)
|
// ex.: tickMsecs = 29(,296875)
|
||||||
|
|
||||||
// use a minimum of 10 msecs per tick for good entropy
|
// use a minimum of 10 msecs per tick for good entropy
|
||||||
// it would take 21 seconds to get full 256 bits of entropy with 10msec ticks
|
// it would take 21 seconds to get full 256 bits of entropy with 10msec ticks
|
||||||
|
@ -29,33 +27,51 @@ func getTickDuration() time.Duration {
|
||||||
tickMsecs = 10
|
tickMsecs = 10
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Duration(tickMsecs * int64(time.Millisecond))
|
return time.Duration(tickMsecs) * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|
||||||
// tickFeeder is a really simple entropy feeder that adds the least significant bit of the current nanosecond unixtime to its pool every time it 'ticks'.
|
// tickFeeder is a really simple entropy feeder that adds the least significant bit of the current nanosecond unixtime to its pool every time it 'ticks'.
|
||||||
// The more work the program does, the better the quality, as the internal schedular cannot immediately run the goroutine when it's ready.
|
// The more work the program does, the better the quality, as the internal schedular cannot immediately run the goroutine when it's ready.
|
||||||
func tickFeeder() {
|
func tickFeeder(ctx context.Context) error {
|
||||||
|
|
||||||
var value int64
|
var value int64
|
||||||
var pushes int
|
var pushes int
|
||||||
feeder := NewFeeder()
|
feeder := NewFeeder()
|
||||||
|
defer feeder.CloseFeeder()
|
||||||
|
|
||||||
|
tickDuration := getTickFeederTickDuration()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
// wait for tick
|
||||||
case <-time.After(tickDuration):
|
time.Sleep(tickDuration)
|
||||||
|
|
||||||
value = (value << 1) | (time.Now().UnixNano() % 2)
|
// add tick value
|
||||||
|
value = (value << 1) | (time.Now().UnixNano() % 2)
|
||||||
|
pushes++
|
||||||
|
|
||||||
pushes++
|
if pushes >= 64 {
|
||||||
if pushes >= 64 {
|
// convert to []byte
|
||||||
feeder.SupplyEntropyAsInt(value, 8)
|
b := make([]byte, 8)
|
||||||
pushes = 0
|
binary.LittleEndian.PutUint64(b, uint64(value))
|
||||||
|
// reset
|
||||||
|
pushes = 0
|
||||||
|
|
||||||
|
// feed
|
||||||
|
select {
|
||||||
|
case feeder.input <- &entropyData{
|
||||||
|
data: b,
|
||||||
|
entropy: 8,
|
||||||
|
}:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// check if are done
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
tickDuration = getTickDuration()
|
|
||||||
|
|
||||||
case <-shutdownSignal:
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue