/** * @brief One of these in the whole process. * It takes pending translations from translationq, * translates the message, saves the translation, * then calls all the Finished events. * The translation remains in the translations * dictionary in case the translation is needed * again. */ private static void TranslatorThread() { Monitor.Enter(queuelock); try { while (runthread) { Watchdog.UpdateThread(); // take old translations out of cache DateTime utcnow = DateTime.UtcNow; LinkedListNode <Translation> lln; Translation val; while ((lln = oldtranslations.First) != null) { val = lln.Value; if (val.xlation == null) { break; } if ((numcachedchars < CACHE_CHARS) && (utcnow.Subtract(val.lastuse).TotalSeconds < CACHE_SECS)) { break; } numcachedchars -= val.key.Length; oldtranslations.Remove(lln); translations.Remove(val.key); } // wait for something to process and dequeue it if (translationq.Count == 0) { Monitor.Wait(queuelock, WD_TIMEOUT_MS / 2); continue; } val = translationq.Dequeue(); // do the translation (might take a few seconds) string incorig, xlation; Monitor.Exit(queuelock); try { try { xlation = service.Translate(val.agentID, val.srclc, val.dstlc, val.original); if (xlation == null) { throw new ApplicationException("result null"); } xlation = xlation.Trim(); if (xlation == "") { throw new ApplicationException("result empty"); } } catch (Exception e) { m_log.Warn("[Translator]: failed " + val.srclc + " -> " + val.dstlc + ": ", e); m_log.Info("[Translator]: original=<" + val.original + ">"); xlation = null; } // original text with language code if translation failed incorig = "[[[" + val.srclc + "]]]" + val.original; if (xlation == null) { xlation = incorig; } else { incorig = xlation + "\n" + incorig; } } finally { Monitor.Enter(queuelock); } // mark entry in translations dictionary as completed val.xlation = xlation; val.agentID = null; // see if anything was waiting for the translation to complete // if so, clear out the list and call them whilst unlocked TranslatorFinished tfs = val.onFinished; if (tfs != null) { val.onFinished = null; Monitor.Exit(queuelock); try { do { tfs.finished(tfs.showorig ? incorig : xlation); tfs = tfs.nextfin; } while (tfs != null); } finally { Monitor.Enter(queuelock); } } } } catch (Exception e) { m_log.Error("[Translator]: Error in translator thread", e); } finally { runthread = false; Monitor.Exit(queuelock); Watchdog.RemoveThread(true); } }
/** * @brief Start translating the message, call finished when done. */ private static void Translate(string srclc, string dstlc, bool showorig, string message, string agentID, ITranslatorFinished finished) { message = message.Trim(); // key for the translations dictionary string key = srclc + ":" + dstlc + ":" + message; string xlation = null; lock (queuelock) { if (runthread) { // see if we already have this translation either done or in progress Translation val; if (translations.TryGetValue(key, out val)) { oldtranslations.Remove(val.uselink); } else { // no, queue the translation for processing val = new Translation(); val.agentID = agentID; val.key = key; translations[key] = val; translationq.Enqueue(val); numcachedchars += key.Length; Monitor.PulseAll(queuelock); } // make it the newest translation around val.lastuse = DateTime.UtcNow; oldtranslations.AddLast(val.uselink); // if translation in progress, queue 'finished' for processing when done xlation = val.xlation; if (xlation == null) { TranslatorFinished tf = new TranslatorFinished(); tf.nextfin = val.onFinished; tf.finished = finished; tf.showorig = showorig; val.onFinished = tf; } } else { // translation thread not running (crashed or whatever) xlation = "[[[" + srclc + "]]]" + message; showorig = false; } } // if translation completed, call finished right now if (xlation != null) { if (showorig) { xlation += "\n[[[" + srclc + "]]]" + message; } finished(xlation); } }