public static void Run() { // If there is mail in the outbound mail queue, do not add more. (This is a primitive // protection against dupes. Better will come that doesn't force idle time like this.) lock (lockObject) { if (mailQueueSize > 0) { // return; } } // Look for a mail to process, or keep processing. OutboundMails mails = OutboundMails.GetTopUnprocessed(100); mailQueueSize = 0; if (mails.Count == 0) { // no unprocessed mail, everybody's happy return; } int maxBatchSize = 20; // Total number of recipients to process across all mails foreach (OutboundMail mail in mails) { // If we have already processed past our limit, return for now. if (maxBatchSize < 1) { return; } // We are continuing to process, and starting with a fresh outbound mail. // Is this the first time we touch this mail? if (mail.StartProcessDateTime.Year < 2000) { // Yes! Mark it as started and mail our author. mail.StartProcessDateTime = DateTime.Now; if (mail.MailType == (int)TypedMailTemplate.TemplateType.MemberMail || mail.MailType == (int)TypedMailTemplate.TemplateType.OfficerMail) // TODO: Set special flag { string mailBody = string.Empty; new MailTransmitter( Strings.MailSenderName, Strings.MailSenderAddress, "Mail transmission begins: " + mail.Title, mailBody, Person.FromIdentity(mail.AuthorPersonId), true).Send(); } } int batchSize = maxBatchSize; // If we are debugging, and stepping through this program, avoid the misery of many simultaneous threads. if (Debugger.IsAttached) { batchSize = 1; // Do NOT multithread while debugging } while (true) { if (!Debugger.IsAttached) { // HeartBeater.Instance.Beat(); //Tick the heartbeat to stop exernal restart if this takes a lot of time, but only if not debugging. } OutboundMailRecipients recipients = mail.GetNextRecipientBatch(batchSize); maxBatchSize -= recipients.Count; // Decrement number of recipients to process on next mail, if any if (recipients.Count == 0) { // This mail is complete! mail.SetProcessed(); if (mail.MailType == (int)TypedMailTemplate.TemplateType.MemberMail || mail.MailType == (int)TypedMailTemplate.TemplateType.OfficerMail) // TODO: Set special flag { string body = "Your mail has completed transmitting to " + mail.RecipientCount.ToString("#,##0") + " intended recipients. Out of these, " + mail.RecipientsFail.ToString("#,##0") + " failed because of invalid, empty, or otherwise bad e-mail addresses. These people have not received your message.\r\n"; new MailTransmitter( Strings.MailSenderName, Strings.MailSenderAddress, "Mail transmission completed: " + mail.Title, body, Person.FromIdentity(mail.AuthorPersonId), true).Send(); } if (maxBatchSize < 1) { return; } break; //the while loop } List <IAsyncResult> sendInProgress = new List <IAsyncResult>(); List <WaitHandle> waitHandlesList = new List <WaitHandle>(); foreach (OutboundMailRecipient recipient in recipients) { // Skip known invalid mail addresses if (recipient.Person != null && (recipient.Person.EMailIsInvalid || recipient.Person.MailUnreachable)) { lock (lockObject) { recipient.Delete(); recipient.OutboundMail.IncrementFailures(); } continue; } // Start the transmission process, asynchronously lock (lockObject) { MailTransmissionDelegate asyncTransmitter = TransmitOneMail; MailTransmissionAsyncState asyncState = new MailTransmissionAsyncState(); asyncState.dlgt = asyncTransmitter; asyncState.recipient = recipient; IAsyncResult asyncResult = asyncTransmitter.BeginInvoke(recipient, MailSent, asyncState); sendInProgress.Add(asyncResult); waitHandlesList.Add(asyncResult.AsyncWaitHandle); mailQueueSize++; } Thread.Sleep(25); // Allow some time } // now wait for them to finish; int numberStillExecuting = sendInProgress.Count; int numberExecutingLast = numberStillExecuting + 1; DateTime lastProgress = DateTime.Now; while (numberStillExecuting > 0) { WaitHandle.WaitAny(waitHandlesList.ToArray(), 100, true); lock (lockObject) { numberStillExecuting = 0; waitHandlesList = new List <WaitHandle>(); for (int i = 0; i < sendInProgress.Count; ++i) { IAsyncResult iares = sendInProgress[i]; MailTransmissionAsyncState asyncState = (MailTransmissionAsyncState)iares.AsyncState; if (asyncState.dlgt != null) { if (!asyncState.callbackCompleted) { waitHandlesList.Add(iares.AsyncWaitHandle); numberStillExecuting++; } else { //Just finalised if (asyncState.exception != null) { if (asyncState.exception is RetryRecipientException) { //Failed in sending due to some reason that can clear up by itself. asyncState.dlgt = null; } else { //Make sure recipient is deleted try { asyncState.recipient.Delete(); // if RemoveRecipientException everything went ok except for the removal if (asyncState.exception is RemoveRecipientException) { asyncState.recipient.OutboundMail.IncrementSuccesses(); } asyncState.dlgt = null; // mark as done; mailQueueSize--; } catch { //Keep looping until recipient removed numberStillExecuting++; } } } } } } } if (numberExecutingLast != numberStillExecuting) { lastProgress = DateTime.Now; } numberExecutingLast = numberStillExecuting; if (lastProgress.AddSeconds(60 * 2) < DateTime.Now) { // since last change, something must have hanged lock (lockObject) { mailQueueSize = -1000; } throw new Exception("Timeout in MailProcessor"); } } } } }
private void BuildTable() { tab.Rows.Clear(); OutboundMails mails = OutboundMails.FromArray(PirateDb.GetDatabase().GetOutboundMailQueue(500)); TableRow row = new TableRow(); tab.Rows.Add(row); TableCell c = new TableCell(); row.Cells.Add(c); c.Text = "Template"; c = new TableCell(); row.Cells.Add(c); c.Text = "Title"; c = new TableCell(); row.Cells.Add(c); c.Text = "Release time"; c = new TableCell(); row.Cells.Add(c); c.Text = "Time until release"; c = new TableCell(); row.Cells.Add(c); c.Text = "Counts"; c = new TableCell(); row.Cells.Add(c); c.Text = "Started"; foreach (OutboundMail mail in mails) { TypedMailTemplate tmpl = null; if (mail.MailType != 0) { tmpl = TypedMailTemplate.FromName(mail.Title); tmpl.Initialize("SE", mail.OrganizationId, mail, ""); tmpl.InsertAllPlaceHoldersToTemplate(); } row = new TableRow(); tab.Rows.Add(row); if (mail.MailType != 0) { c = new TableCell(); row.Cells.Add(c); c.Text = mail.Title; c = new TableCell(); row.Cells.Add(c); c.Text = tmpl.Template.TemplateTitleText; } else { c = new TableCell(); row.Cells.Add(c); c.Text = "none"; c = new TableCell(); row.Cells.Add(c); c.Text = mail.Title; } c = new TableCell(); row.Cells.Add(c); c.Text = mail.ReleaseDateTime.ToString(); if ((new DateTime(1970, 1, 1)) == mail.StartProcessDateTime && mail.ReleaseDateTime < DateTime.Now) { c.ForeColor = System.Drawing.Color.Red; c.Font.Bold = true; } c = new TableCell(); row.Cells.Add(c); c.Text = Math.Round((DateTime.Now.Subtract(mail.ReleaseDateTime).TotalMinutes)).ToString(); c = new TableCell(); row.Cells.Add(c); c.Text = mail.RecipientCount.ToString(); c.Text += " / " + mail.RecipientsSuccess.ToString(); c.Text += " / " + mail.RecipientsFail.ToString(); c = new TableCell(); row.Cells.Add(c); c.Text = mail.StartProcessDateTime.ToString(); } }