package main import ( "net" "sync" "time" ) // threadWork processes a single work item for a thread. func threadWork(dialer *net.Dialer) bool { readTimeout := CfgGetDurationMS("read-timeout-ms") task, delay := CreateTask() if task == nil { if delay > 0 { log("thread", 3, "no endpoints available, sleeping for %v", delay) time.Sleep(delay) return true } else { log("thread", 3, "no endpoints available, stopping thread loop") return false } } conn, err := dialer.Dial("tcp", task.e.String()) if err != nil { task.Event(TE_NoResponse) log("thread", 2, "cannot connect to \"%v\": %v", task.e, err.Error()) return true } defer conn.Close() task.conn = conn task.Event(TN_Connected) conn.SetReadDeadline(time.Now().Add(readTimeout)) // should be just before Send() call... log("thread", 2, "trying %v:%v on \"%v\"", task.login, task.password, task.e) // TODO: multiple services (currently just WinBox) res, err := TryLogin(task, conn) if err != nil { task.Event(TE_ProtocolError) } else { if res && err == nil { task.EventWithParm(TE_Good, task.login) } else { task.EventWithParm(TE_Bad, task.login) } } return true } // threadLoop calls threadWork in a loop, until the endpoints are exhausted, // a pause/stop signal has been raised, or an exception has occurred in threadWork. func threadLoop(dialer *net.Dialer) { for threadWork(dialer) { // TODO: pause/stop signal // TODO: exception handling } } // threadEntryPoint is the main entrypoint for a work thread. func threadEntryPoint(c chan bool, threadIdx int, wg *sync.WaitGroup) { <-c log("thread", 3, "starting loop for thread %v", threadIdx) connectTimeout := time.Duration(CfgGetInt("connect-timeout-ms")) * time.Millisecond dialer := net.Dialer{Timeout: connectTimeout, KeepAlive: -1} threadLoop(&dialer) log("thread", 3, "exiting thread %v", threadIdx) wg.Done() } // InitializeThreads creates and starts up all threads. func InitializeThreads() *sync.WaitGroup { numThreads := CfgGetInt("threads") failIf(numThreads > maxSafeThreads, "too many threads (max %v)", maxSafeThreads) log("thread", 0, "initializing %v threads", numThreads) c := make(chan bool) var wg sync.WaitGroup for i := 1; i <= numThreads; i++ { wg.Add(1) go threadEntryPoint(c, i, &wg) } threadDelay := CfgGetDurationMS("thread-delay-ms") log("thread", 0, "starting %v threads", numThreads) for i := 1; i <= numThreads; i++ { c <- true if threadDelay > 0 { time.Sleep(threadDelay) } } log("thread", 0, "started") return &wg } // WaitForThreads enters a wait state and keeps it until // all threads have exited. func WaitForThreads(wg *sync.WaitGroup) { log("thread", 1, "waiting for threads") wg.Wait() log("thread", 1, "finished waiting for threads") }