Task: Banker's algorithm




Go: package bank

import (
    "bytes"
    "errors"
    "fmt"
    "log"
    "sort"
    "sync"
)

type PID string
type RID string
type RMap map[RID]int


func (m RMap) String() string {
    rs := make([]string, len(m))
    i := 0
    for r := range m {
        rs[i] = string(r)
        i++
    }
    sort.Strings(rs)
    var b bytes.Buffer
    b.WriteString("{")
    for _, r := range rs {
        fmt.Fprintf(&b, "%q: %d, ", r, m[RID(r)])
    }
    bb := b.Bytes()
    if len(bb) > 1 {
        bb[len(bb)-2] = '}'
    }
    return string(bb)
}

type Bank struct {
    available  RMap
    max        map[PID]RMap
    allocation map[PID]RMap
    sync.Mutex
}

func (b *Bank) need(p PID, r RID) int {
    return b.max[p][r] - b.allocation[p][r]
}

func New(available RMap) (b *Bank, err error) {
    for r, a := range available {
        if a < 0 {
            return nil, fmt.Errorf("negative resource %s: %d", r, a)
        }
    }
    return &Bank{
        available:  available,
        max:        map[PID]RMap{},
        allocation: map[PID]RMap{},
    }, nil
}

func (b *Bank) NewProcess(p PID, max RMap) (err error) {
    b.Lock()
    defer b.Unlock()
    if _, ok := b.max[p]; ok {
        return fmt.Errorf("process %s already registered", p)
    }
    for r, m := range max {
        switch a, ok := b.available[r]; {
        case !ok:
            return fmt.Errorf("resource %s unknown", r)
        case m > a:
            return fmt.Errorf("resource %s: process %s max %d > available %d",
                r, p, m, a)
        }
    }
    b.max[p] = max
    b.allocation[p] = RMap{}
    return
}

func (b *Bank) Request(pid PID, change RMap) (err error) {
    b.Lock()
    defer b.Unlock()
    if _, ok := b.max[pid]; !ok {
        return fmt.Errorf("process %s unknown", pid)
    }
    for r, c := range change {
        if c < 0 {
            return errors.New("decrease not allowed")
        }
        if _, ok := b.available[r]; !ok {
            return fmt.Errorf("resource %s unknown", r)
        }
        if c > b.need(pid, r) {
            return errors.New("increase exceeds declared max")
        }
    }
    
    
    for r, c := range change {
        b.allocation[pid][r] += c 
    }
    defer func() {
        if err != nil { 
            for r, c := range change {
                b.allocation[pid][r] -= c 
            }
        }
    }()
    
    
    cash := RMap{}
    for r, a := range b.available {
        cash[r] = a
    }
    perm := make([]PID, len(b.allocation))
    i := 1
    for pr, a := range b.allocation {
        if pr == pid {
            perm[0] = pr
        } else {
            perm[i] = pr
            i++
        }
        for r, a := range a {
            cash[r] -= a
        }
    }
    ret := RMap{}  
    m := len(perm) 
    for {
        
        h := 0
    h:
        for ; ; h++ {
            if h == m {
                
                return errors.New("request would make deadlock possible")
            }
            for r := range b.available {
                if b.need(perm[h], r) > cash[r]+ret[r] {
                    
                    continue h
                }
            }
            
            log.Println(" ", perm[h], "could terminate")
            
            break
        }
        if h == 0 { 
            
            
            return nil
        }
        for r, a := range b.allocation[perm[h]] {
            ret[r] += a
        }
        m--
        perm[h] = perm[m]
    }
}