Index: /pjproject/trunk/pjlib/include/pj/ioqueue.h
===================================================================
--- /pjproject/trunk/pjlib/include/pj/ioqueue.h	(revision 5193)
+++ /pjproject/trunk/pjlib/include/pj/ioqueue.h	(revision 5194)
@@ -304,4 +304,26 @@
 #endif
 
+
+/**
+ * This macro specifies the maximum event candidates collected by each
+ * polling thread to be able to reach maximum number of processed events
+ * (i.e: PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL) in each poll cycle.
+ * An event candidate will be dispatched to application as event unless
+ * it is already being dispatched by other polling thread. So in order to
+ * anticipate such race condition, each poll operation should collects its
+ * event candidates more than PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL, the
+ * recommended value is (PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL *
+ * number of polling threads).
+ *
+ * The value is only meaningfull when specified during PJLIB build and
+ * is only effective on multiple polling threads environment.
+ */
+#if !defined(PJ_IOQUEUE_MAX_CAND_EVENTS) || \
+    PJ_IOQUEUE_MAX_CAND_EVENTS < PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL
+#   undef  PJ_IOQUEUE_MAX_CAND_EVENTS
+#   define PJ_IOQUEUE_MAX_CAND_EVENTS	PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL
+#endif
+
+
 /**
  * When this flag is specified in ioqueue's recv() or send() operations,
@@ -503,4 +525,15 @@
  */
 PJ_DECL(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key);
+
+/**
+ * Try to acquire the key's mutex. When the key's concurrency is disabled, 
+ * application may call this function to synchronize its operation
+ * with the key's callback.
+ *
+ * @param key	    The key that was previously obtained from registration.
+ *
+ * @return	    PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_trylock_key(pj_ioqueue_key_t *key);
 
 /**
Index: /pjproject/trunk/pjlib/src/pj/ioqueue_common_abs.c
===================================================================
--- /pjproject/trunk/pjlib/src/pj/ioqueue_common_abs.c	(revision 5193)
+++ /pjproject/trunk/pjlib/src/pj/ioqueue_common_abs.c	(revision 5194)
@@ -196,12 +196,18 @@
  * framework.
  */
-void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h)
-{
-    /* Lock the key. */
-    pj_ioqueue_lock_key(h);
+pj_bool_t ioqueue_dispatch_write_event( pj_ioqueue_t *ioqueue,
+				        pj_ioqueue_key_t *h)
+{
+    pj_status_t rc;
+
+    /* Try lock the key. */
+    rc = pj_ioqueue_trylock_key(h);
+    if (rc != PJ_SUCCESS) {
+	return PJ_FALSE;
+    }
 
     if (IS_CLOSING(h)) {
 	pj_ioqueue_unlock_key(h);
-	return;
+	return PJ_TRUE;
     }
 
@@ -418,17 +424,25 @@
          */
 	pj_ioqueue_unlock_key(h);
-    }
-}
-
-void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
+
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+pj_bool_t ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue,
+				       pj_ioqueue_key_t *h )
 {
     pj_status_t rc;
 
-    /* Lock the key. */
-    pj_ioqueue_lock_key(h);
+    /* Try lock the key. */
+    rc = pj_ioqueue_trylock_key(h);
+    if (rc != PJ_SUCCESS) {
+	return PJ_FALSE;
+    }
 
     if (IS_CLOSING(h)) {
 	pj_ioqueue_unlock_key(h);
-	return;
+	return PJ_TRUE;
     }
 
@@ -605,14 +619,23 @@
          */
 	pj_ioqueue_unlock_key(h);
-    }
-}
-
-
-void ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue, 
-                                       pj_ioqueue_key_t *h )
+
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+
+pj_bool_t ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue,
+					    pj_ioqueue_key_t *h )
 {
     pj_bool_t has_lock;
-
-    pj_ioqueue_lock_key(h);
+    pj_status_t rc;
+
+    /* Try lock the key. */
+    rc = pj_ioqueue_trylock_key(h);
+    if (rc != PJ_SUCCESS) {
+	return PJ_FALSE;
+    }
 
     if (!h->connecting) {
@@ -622,10 +645,10 @@
          */
 	pj_ioqueue_unlock_key(h);
-        return;
+	return PJ_TRUE;
     }
 
     if (IS_CLOSING(h)) {
 	pj_ioqueue_unlock_key(h);
-	return;
+	return PJ_TRUE;
     }
 
@@ -669,4 +692,6 @@
 	pj_ioqueue_unlock_key(h);
     }
+
+    return PJ_TRUE;
 }
 
@@ -1325,4 +1350,12 @@
 }
 
+PJ_DEF(pj_status_t) pj_ioqueue_trylock_key(pj_ioqueue_key_t *key)
+{
+    if (key->grp_lock)
+	return pj_grp_lock_tryacquire(key->grp_lock);
+    else
+	return pj_lock_tryacquire(key->lock);
+}
+
 PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key)
 {
Index: /pjproject/trunk/pjlib/src/pj/ioqueue_epoll.c
===================================================================
--- /pjproject/trunk/pjlib/src/pj/ioqueue_epoll.c	(revision 5193)
+++ /pjproject/trunk/pjlib/src/pj/ioqueue_epoll.c	(revision 5194)
@@ -652,10 +652,11 @@
 PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
 {
-    int i, count, processed;
+    int i, count, event_cnt, processed_cnt;
     int msec;
     //struct epoll_event *events = ioqueue->events;
     //struct queue *queue = ioqueue->queue;
-    struct epoll_event events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
-    struct queue queue[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
+    enum { MAX_EVENTS = PJ_IOQUEUE_MAX_CAND_EVENTS };
+    struct epoll_event events[MAX_EVENTS];
+    struct queue queue[MAX_EVENTS];
     pj_timestamp t1, t2;
     
@@ -668,5 +669,5 @@
  
     //count = os_epoll_wait( ioqueue->epfd, events, ioqueue->max, msec);
-    count = os_epoll_wait( ioqueue->epfd, events, PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL, msec);
+    count = os_epoll_wait( ioqueue->epfd, events, MAX_EVENTS, msec);
     if (count == 0) {
 #if PJ_IOQUEUE_HAS_SAFE_UNREG
@@ -695,5 +696,5 @@
     pj_lock_acquire(ioqueue->lock);
 
-    for (processed=0, i=0; i<count; ++i) {
+    for (event_cnt=0, i=0; i<count; ++i) {
 	pj_ioqueue_key_t *h = (pj_ioqueue_key_t*)(epoll_data_type)
 				events[i].epoll_data;
@@ -710,7 +711,7 @@
 	    increment_counter(h);
 #endif
-	    queue[processed].key = h;
-	    queue[processed].event_type = READABLE_EVENT;
-	    ++processed;
+	    queue[event_cnt].key = h;
+	    queue[event_cnt].event_type = READABLE_EVENT;
+	    ++event_cnt;
 	    continue;
 	}
@@ -724,7 +725,7 @@
 	    increment_counter(h);
 #endif
-	    queue[processed].key = h;
-	    queue[processed].event_type = WRITEABLE_EVENT;
-	    ++processed;
+	    queue[event_cnt].key = h;
+	    queue[event_cnt].event_type = WRITEABLE_EVENT;
+	    ++event_cnt;
 	    continue;
 	}
@@ -739,7 +740,7 @@
 	    increment_counter(h);
 #endif
-	    queue[processed].key = h;
-	    queue[processed].event_type = WRITEABLE_EVENT;
-	    ++processed;
+	    queue[event_cnt].key = h;
+	    queue[event_cnt].event_type = WRITEABLE_EVENT;
+	    ++event_cnt;
 	    continue;
 	}
@@ -759,19 +760,19 @@
 		increment_counter(h);
 #endif
-		queue[processed].key = h;
-		queue[processed].event_type = EXCEPTION_EVENT;
-		++processed;
+		queue[event_cnt].key = h;
+		queue[event_cnt].event_type = EXCEPTION_EVENT;
+		++event_cnt;
 	    } else if (key_has_pending_read(h) || key_has_pending_accept(h)) {
 #if PJ_IOQUEUE_HAS_SAFE_UNREG
 		increment_counter(h);
 #endif
-		queue[processed].key = h;
-		queue[processed].event_type = READABLE_EVENT;
-		++processed;
+		queue[event_cnt].key = h;
+		queue[event_cnt].event_type = READABLE_EVENT;
+		++event_cnt;
 	    }
 	    continue;
 	}
     }
-    for (i=0; i<processed; ++i) {
+    for (i=0; i<event_cnt; ++i) {
 	if (queue[i].key->grp_lock)
 	    pj_grp_lock_add_ref_dbg(queue[i].key->grp_lock, "ioqueue", 0);
@@ -784,20 +785,29 @@
     PJ_RACE_ME(5);
 
+    processed_cnt = 0;
+
     /* Now process the events. */
-    for (i=0; i<processed; ++i) {
-	switch (queue[i].event_type) {
-        case READABLE_EVENT:
-            ioqueue_dispatch_read_event(ioqueue, queue[i].key);
-            break;
-        case WRITEABLE_EVENT:
-            ioqueue_dispatch_write_event(ioqueue, queue[i].key);
-            break;
-        case EXCEPTION_EVENT:
-            ioqueue_dispatch_exception_event(ioqueue, queue[i].key);
-            break;
-        case NO_EVENT:
-            pj_assert(!"Invalid event!");
-            break;
-        }
+    for (i=0; i<event_cnt; ++i) {
+
+	/* Just do not exceed PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL */
+	if (processed_cnt < PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL) {
+	    switch (queue[i].event_type) {
+	    case READABLE_EVENT:
+		if (ioqueue_dispatch_read_event(ioqueue, queue[i].key))
+		    ++processed_cnt;
+		break;
+	    case WRITEABLE_EVENT:
+		if (ioqueue_dispatch_write_event(ioqueue, queue[i].key))
+		    ++processed_cnt;
+		break;
+	    case EXCEPTION_EVENT:
+		if (ioqueue_dispatch_exception_event(ioqueue, queue[i].key))
+		    ++processed_cnt;
+		break;
+	    case NO_EVENT:
+		pj_assert(!"Invalid event!");
+		break;
+	    }
+	}
 
 #if PJ_IOQUEUE_HAS_SAFE_UNREG
@@ -813,7 +823,10 @@
      * When epoll returns > 0 but no descriptors are actually set!
      */
-    if (count > 0 && !processed && msec > 0) {
+    if (count > 0 && !event_cnt && msec > 0) {
 	pj_thread_sleep(msec);
     }
+
+    TRACE_((THIS_FILE, "     poll: count=%d events=%d processed=%d",
+		       count, event_cnt, processed_cnt));
 
     pj_get_timestamp(&t1);
@@ -821,5 +834,5 @@
 		       processed, pj_elapsed_usec(&t2, &t1)));
 
-    return processed;
-}
-
+    return processed_cnt;
+}
+
Index: /pjproject/trunk/pjlib/src/pj/ioqueue_select.c
===================================================================
--- /pjproject/trunk/pjlib/src/pj/ioqueue_select.c	(revision 5193)
+++ /pjproject/trunk/pjlib/src/pj/ioqueue_select.c	(revision 5194)
@@ -334,4 +334,11 @@
     PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET &&
                      cb && p_key, PJ_EINVAL);
+
+    /* On platforms with fd_set containing fd bitmap such as *nix family,
+     * avoid potential memory corruption caused by select() when given
+     * an fd that is higher than FD_SETSIZE.
+     */
+    if (sizeof(fd_set) < FD_SETSIZE && sock >= PJ_IOQUEUE_MAX_HANDLES)
+	return PJ_ETOOBIG;
 
     pj_lock_acquire(ioqueue->lock);
@@ -832,11 +839,12 @@
     pj_fd_set_t rfdset, wfdset, xfdset;
     int nfds;
-    int count, i, counter;
+    int i, count, event_cnt, processed_cnt;
     pj_ioqueue_key_t *h;
+    enum { MAX_EVENTS = PJ_IOQUEUE_MAX_CAND_EVENTS };
     struct event
     {
         pj_ioqueue_key_t	*key;
         enum ioqueue_event_type  event_type;
-    } event[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
+    } event[MAX_EVENTS];
 
     PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL);
@@ -890,6 +898,4 @@
     else if (count < 0)
 	return -pj_get_netos_error();
-    else if (count > PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL)
-        count = PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL;
 
     /* Scan descriptor sets for event and add the events in the event
@@ -899,11 +905,13 @@
     pj_lock_acquire(ioqueue->lock);
 
-    counter = 0;
+    event_cnt = 0;
 
     /* Scan for writable sockets first to handle piggy-back data
      * coming with accept().
      */
-    h = ioqueue->active_list.next;
-    for ( ; h!=&ioqueue->active_list && counter<count; h = h->next) {
+    for (h = ioqueue->active_list.next;
+	 h != &ioqueue->active_list && event_cnt < MAX_EVENTS;
+	 h = h->next)
+    {
 
 	if ( (key_has_pending_write(h) || key_has_pending_connect(h))
@@ -913,7 +921,7 @@
 	    increment_counter(h);
 #endif
-            event[counter].key = h;
-            event[counter].event_type = WRITEABLE_EVENT;
-            ++counter;
+            event[event_cnt].key = h;
+            event[event_cnt].event_type = WRITEABLE_EVENT;
+            ++event_cnt;
         }
 
@@ -921,29 +929,29 @@
 	if ((key_has_pending_read(h) || key_has_pending_accept(h))
             && PJ_FD_ISSET(h->fd, &rfdset) && !IS_CLOSING(h) &&
-	    counter<count)
+	    event_cnt < MAX_EVENTS)
         {
 #if PJ_IOQUEUE_HAS_SAFE_UNREG
 	    increment_counter(h);
 #endif
-            event[counter].key = h;
-            event[counter].event_type = READABLE_EVENT;
-            ++counter;
+            event[event_cnt].key = h;
+            event[event_cnt].event_type = READABLE_EVENT;
+            ++event_cnt;
 	}
 
 #if PJ_HAS_TCP
         if (key_has_pending_connect(h) && PJ_FD_ISSET(h->fd, &xfdset) &&
-	    !IS_CLOSING(h) && counter<count) 
+	    !IS_CLOSING(h) && event_cnt < MAX_EVENTS)
 	{
 #if PJ_IOQUEUE_HAS_SAFE_UNREG
 	    increment_counter(h);
 #endif
-            event[counter].key = h;
-            event[counter].event_type = EXCEPTION_EVENT;
-            ++counter;
+            event[event_cnt].key = h;
+            event[event_cnt].event_type = EXCEPTION_EVENT;
+            ++event_cnt;
         }
 #endif
     }
 
-    for (i=0; i<counter; ++i) {
+    for (i=0; i<event_cnt; ++i) {
 	if (event[i].key->grp_lock)
 	    pj_grp_lock_add_ref_dbg(event[i].key->grp_lock, "ioqueue", 0);
@@ -956,36 +964,45 @@
     PJ_RACE_ME(5);
 
-    count = counter;
+    processed_cnt = 0;
 
     /* Now process all events. The dispatch functions will take care
      * of locking in each of the key
      */
-    for (counter=0; counter<count; ++counter) {
-        switch (event[counter].event_type) {
-        case READABLE_EVENT:
-            ioqueue_dispatch_read_event(ioqueue, event[counter].key);
-            break;
-        case WRITEABLE_EVENT:
-            ioqueue_dispatch_write_event(ioqueue, event[counter].key);
-            break;
-        case EXCEPTION_EVENT:
-            ioqueue_dispatch_exception_event(ioqueue, event[counter].key);
-            break;
-        case NO_EVENT:
-            pj_assert(!"Invalid event!");
-            break;
-        }
-
-#if PJ_IOQUEUE_HAS_SAFE_UNREG
-	decrement_counter(event[counter].key);
-#endif
-
-	if (event[counter].key->grp_lock)
-	    pj_grp_lock_dec_ref_dbg(event[counter].key->grp_lock,
+    for (i=0; i<event_cnt; ++i) {
+
+	/* Just do not exceed PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL */
+	if (processed_cnt < PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL) {
+	    switch (event[i].event_type) {
+	    case READABLE_EVENT:
+		if (ioqueue_dispatch_read_event(ioqueue, event[i].key))
+		    ++processed_cnt;
+		break;
+	    case WRITEABLE_EVENT:
+		if (ioqueue_dispatch_write_event(ioqueue, event[i].key))
+		    ++processed_cnt;
+		break;
+	    case EXCEPTION_EVENT:
+		if (ioqueue_dispatch_exception_event(ioqueue, event[i].key))
+		    ++processed_cnt;
+		break;
+	    case NO_EVENT:
+		pj_assert(!"Invalid event!");
+		break;
+	    }
+	}
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	decrement_counter(event[i].key);
+#endif
+
+	if (event[i].key->grp_lock)
+	    pj_grp_lock_dec_ref_dbg(event[i].key->grp_lock,
 	                            "ioqueue", 0);
     }
 
-
-    return count;
-}
-
+    TRACE__((THIS_FILE, "     poll: count=%d events=%d processed=%d",
+	     count, event_cnt, processed_cnt));
+
+    return processed_cnt;
+}
+
