package main // thread.go: handling of worker threads import ( "sync" "time" ) func init() { registerParam("threads", 3, "how many threads to use") registerParam("thread-delay-ms", 10, "separate threads at startup for this amount of ms") // using a very high limit for now, but this should actually be set to -1 registerParam("task-max-deferrals", 30000, "how many deferrals are allowed for a single task. -1 to disable") registerAlias("t", "threads") } // maxSafeThreads is a safeguard to prevent creation of too many threads at once. const maxSafeThreads = 5000 // ThreadService creates, starts up and waits for all threads. func ThreadService() { numThreads := getParamInt("threads") failIf(numThreads > maxSafeThreads, "too many threads (max %v)", maxSafeThreads) log("thread", 1, "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 := getParamDurationMS("thread-delay-ms") log("thread", 0, "starting %v threads", numThreads) for i := 1; i <= numThreads; i++ { c <- true if threadDelay > 0 { time.Sleep(threadDelay) } } wg.Wait() log("thread", 1, "finished threads") } // 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) for threadWork(threadIdx) { } log("thread", 3, "exiting thread %v", threadIdx) wg.Done() } // threadWork processes a single work item for a thread. func threadWork(threadIdx int) bool { task, delay := CreateTask(threadIdx) if task == nil { if delay > 0 { log("thread", 3, "no work currently available, sleeping for %v in thread %v", delay, threadIdx) time.Sleep(delay) return true } else { log("thread", 2, "no more work for thread %v", threadIdx) return false } } log("thread", 4, "got task %v", task) conn, err := NewConnection(task.e) if err != nil { task.EventWithParm(TE_NoResponse, err) return true } task.Event(TN_Connected) log("thread", 2, "trying %v", task) 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.Event(TE_Bad) } } return true }