// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ssh // Unexported flow control logic copied from x/crypto/ssh import ( "io" "sync" ) const ( // ChannelMaxPacket contains the maximum number of bytes that will be // sent in a single packet. As per RFC 4253, section 6.1, 32k is also // the minimum. ChannelMaxPacket = 1 << 15 // We follow OpenSSH here. ChannelWindowSize = 64 * ChannelMaxPacket ) // Window represents the buffer available to clients // wishing to write to a channel. type Window struct { *sync.Cond win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 writeWaiters int closed bool } // add adds win to the amount of window available // for consumers. func (w *Window) add(win uint32) bool { // a zero sized window adjust is a noop. if win == 0 { return true } w.L.Lock() if w.win+win < win { w.L.Unlock() return false } w.win += win // It is unusual that multiple goroutines would be attempting to reserve // window space, but not guaranteed. Use broadcast to notify all waiters // that additional window is available. w.Broadcast() w.L.Unlock() return true } // close sets the window to closed, so all reservations fail // immediately. func (w *Window) close() { w.L.Lock() w.closed = true w.Broadcast() w.L.Unlock() } // reserve reserves win from the available window capacity. // If no capacity remains, reserve will block. reserve may // return less than requested. func (w *Window) reserve(win uint32) (uint32, error) { var err error w.L.Lock() w.writeWaiters++ w.Broadcast() for w.win == 0 && !w.closed { w.Wait() } w.writeWaiters-- if w.win < win { win = w.win } w.win -= win if w.closed { err = io.EOF } w.L.Unlock() return win, err }