Index: simplenews.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simplenews/simplenews.admin.inc,v
retrieving revision 1.42
diff -u -p -r1.42 simplenews.admin.inc
--- simplenews.admin.inc	7 Dec 2008 19:11:06 -0000	1.42
+++ simplenews.admin.inc	8 Dec 2008 16:10:17 -0000
@@ -883,10 +883,6 @@ function simplenews_admin_settings_mail(
   $address_default = variable_get('site_mail', ini_get('sendmail_from'));
   $form = array();
 
-  $max_time = array(0, 1, 2, 3, 4);
-  for ($i = 5; $i < ini_get('max_execution_time'); $i=$i+5) {
-    $max_time[] = $i;
-  }
   $form['simplenews_mail_backend']['simplenews_use_cron'] = array(
     '#type' => 'checkbox',
     '#title' => t('Use cron to send newsletters'),
@@ -901,7 +897,7 @@ function simplenews_admin_settings_mail(
     '#options' => $throttle,
     '#default_value' => variable_get('simplenews_throttle', 20),
     '#description' => t('Sets the numbers of newsletters sent per cron run. Failure to send will also be counted.') .'<br />'.
-      t('Cron execution must not exceed the PHP maximum exection time of %max seconds. You find the time spend to send emails in the <a href="/admin/reports/dblog">Recent log entries</a>.', array('%max' => ini_get('max_execution_time'))),
+      t('Cron execution must not exceed the PHP maximum execution time of %max seconds. You find the time spend to send emails in the <a href="/admin/reports/dblog">Recent log entries</a>.', array('%max' => ini_get('max_execution_time'))),
   );
   $form['simplenews_mail_backend']['simplenews_spool_expire'] = array(
     '#type' => 'select',
Index: simplenews.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simplenews/simplenews.module,v
retrieving revision 1.169
diff -u -p -r1.169 simplenews.module
--- simplenews.module	7 Dec 2008 19:11:06 -0000	1.169
+++ simplenews.module	8 Dec 2008 16:10:21 -0000
@@ -48,6 +48,17 @@ define('SIMPLENEWS_SPOOL_PENDING', 1);
 define('SIMPLENEWS_SPOOL_SEND', 2);
 
 /**
+ * AFTER EACH 100 NEWSLETTERS
+ * simplenews_mail_send() CHECKS IF LIMITS ARE EXCEEDED
+ */
+define('SIMPLENEWS_SEND_CHECK_INTERVAL', 100);
+
+/**
+ * AT 80% OF PHP MAX EXECUTION TIME EMAIL SENDING IS INTERRUPTED
+ */
+define('SIMPLENEWS_SEND_TIME_LIMIT', 0.8);
+
+/**
  * Implementation of hook_node_info().
  */
 function simplenews_node_info() {
@@ -1730,7 +1741,21 @@ function simplenews_mail_send($nid = NUL
       if ($message['result']) {
         $mail_sent[] = $key;
       }
+
+      // Check every n emails if we exceed the limit.
+      // When PHP maximum execution time is almost elapsed we interrupt
+      // sending. The remainder will be send during the next cron run.
+      if (++$check_counter >= SIMPLENEWS_SEND_CHECK_INTERVAL) {
+        $check_counter = 0;
+        // Break the sending if a percentage of max execution time was exceeded.
+        $elapsed = _simplenews_measure_usec();
+        if ($elapsed > SIMPLENEWS_SEND_TIME_LIMIT * ini_get('max_execution_time')) {
+          watchdog('simplenews', 'Sending interrupted: PHP maximum execution time almost exceeded. Remaining newsletters will be send during the next cron run. If this warning occurs regularly you should reduce the !cron_throttle_setting.', array('!cron_throttle_setting' => l(t('Cron throttle setting'), 'admin/settings/simplenews/mail')), WATCHDOG_WARNING);
+          break;
+        }
+      }
     }
+
     // Mark messages in the spool as sent and update newsletter status.
     if (!empty($mail_sent)) {
       simplenews_mail_spool_update($mail_sent, SIMPLENEWS_SPOOL_SEND);
@@ -1738,7 +1763,7 @@ function simplenews_mail_send($nid = NUL
     simplenews_send_status_update();
 
     // Report sent result and elapsed time
-    watchdog('simplenews', '%count emails sent successfully in %sec seconds.', array('%count' => count($mail_sent), '%sec' => round(_simplenews_measure_usec(), 1)));
+    watchdog('simplenews', '%count emails sent in %sec seconds.', array('%count' => count($mail_sent), '%sec' => round(_simplenews_measure_usec(), 1)));
   }
 }
 
@@ -2222,19 +2247,21 @@ function _simplenews_subscription_confir
 }
 
 /**
- * Helper function to measure elapsed time in microseconds.
+ * Helper function to measure PHP execution time in microseconds.
  *
  * @param bool $start TRUE reset the time and start counting.
- * @return float: elapsed time since start.
+ * @return float: elapsed PHP execution time since start.
  */
 function _simplenews_measure_usec($start = FALSE) {
   static $start_time;
+  $usage = getrusage();
+  $now = (float)($dat["ru_stime.tv_sec"].'.'.$dat["ru_stime.tv_usec"]) + (float)($usage["ru_utime.tv_sec"] .'.'. $usage["ru_utime.tv_usec"]);
   
   if ($start) {
-    $start_time = microtime(TRUE);
+    $start_time = $now;
     return 0;
   }
-  return microtime(TRUE) - $start_time;
+  return $now - $start_time;
 }
 
 /**
