Changeset 3801

Show
Ignore:
Timestamp:
11/10/08 15:08:37
Author:
jdapena
Message:

Complete rework of the lock behavior of Tinymail (camel) imap IDLE
implementation.

The problem we want to fix is: sometimes IDLE thread causes
deathlocks (connect lock, idle lock, g_thread_join, etc).
This happens easily when there are disconnects, or
when we switch fast among folders.

The main changes are:
* Provided a count of reasons to stop idle because of connect lock.
* If a caller has taken connect lock, then it should do the send done

stuff itself.
* Idle thread has the connect lock always when it's in the body of the
loop.
* In any point, if we want to take the connect lock, then we should
stop idle. No partial idle can be alive when we want to connect.
* Camel queue enables/disables the possibility to start idle. That's
for avoiding running IDLE / SEND DONE in the middle of queued
operations.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • releases/modest/diablo-pe2/ChangeLog

    r3799 r3801  
    112008-11-10  Jose Dapena Paz  <jdapena@igalia.com> 
     2 
     3        Complete rework of the lock behavior of Tinymail 
     4        (camel) imap IDLE implementation. 
     5 
     6        The problem we want to fix is: sometimes IDLE thread causes 
     7        deathlocks (connect lock, idle lock, g_thread_join, etc). 
     8        This happens easily when there are disconnects, or 
     9        when we switch fast among folders. 
     10 
     11        The main changes are: 
     12        * Provided a count of reasons to stop idle because of connect lock. 
     13        * If a caller has taken connect lock, then it should do the send done 
     14        stuff itself. 
     15        * Idle thread has the connect lock always when it's in the body of the 
     16        loop. 
     17        * In any point, if we want to  take the connect lock, then we should 
     18        stop idle. No partial idle can be alive when we want to connect. 
     19        * Camel queue enables/disables the possibility to start idle. That's 
     20        for avoiding running IDLE / SEND DONE in the middle of queued 
     21        operations. 
    222 
    323        * Specify with defines the IDLE tick time (time between iterations in 
  • releases/modest/diablo-pe2/libtinymail-camel/camel-lite/camel/camel-service.c

    r3571 r3801  
    6060static gboolean service_disconnect(CamelService *service, gboolean clean, 
    6161                                   CamelException *ex); 
     62static void service_can_idle (CamelService *service, gboolean can_idle); 
    6263static void cancel_connect (CamelService *service); 
    6364static GList *query_auth_types (CamelService *service, CamelException *ex); 
     
    8889        camel_service_class->get_name = get_name; 
    8990        camel_service_class->get_path = get_path; 
     91        camel_service_class->can_idle = service_can_idle; 
    9092} 
    9193 
     
    770772        return ret; 
    771773} 
     774 
     775/** 
     776 * camel_service_can_idle: 
     777 * @service: a #CamelService 
     778 * @can_idle: a #gboolean 
     779 * 
     780 * Sets if service can do idle operations or not. This is for 
     781 * avoiding the service believe it can do idle operations in 
     782 * the middle of queued operations. 
     783 */ 
     784void 
     785camel_service_can_idle (CamelService *service, 
     786                        gboolean can_idle) 
     787{ 
     788        g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL); 
     789 
     790        CSERV_CLASS (service)->can_idle (service, can_idle); 
     791} 
     792 
     793static void 
     794service_can_idle (CamelService *service, 
     795                  gboolean can_idle) 
     796{ 
     797        /* Default implementation is empty */ 
     798} 
  • releases/modest/diablo-pe2/libtinymail-camel/camel-lite/camel/camel-service.h

    r3088 r3801  
    7474        gboolean reconnecting; 
    7575 
     76        gboolean can_idle; 
     77 
    7678        con_op connecting; 
    7779        con_op disconnecting; 
     
    102104                                        gboolean brief); 
    103105        char *    (*get_path)          (CamelService *service); 
     106        void      (*can_idle)          (CamelService *service, gboolean can_idle); 
    104107 
    105108} CamelServiceClass; 
     
    142145                                                      CamelException *ex); 
    143146 
     147void                camel_service_can_idle            (CamelService *service, 
     148                                                       gboolean can_idle); 
     149 
    144150/* Standard Camel function */ 
    145151CamelType camel_service_get_type (void); 
  • releases/modest/diablo-pe2/libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-command.c

    r3646 r3801  
    123123        char *cmd = NULL; 
    124124 
    125         CAMEL_SERVICE_REC_LOCK (store, connect_lock); 
     125        camel_imap_store_stop_idle_connect_lock (store); 
    126126 
    127127        if (fmt) { 
     
    180180        if (!imap_command_start (store, folder, cmd, ex)) { 
    181181                g_free (cmd); 
    182                 CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     182                camel_imap_store_connect_unlock_start_idle (store); 
    183183                return NULL; 
    184184        } 
     
    235235        va_end (ap); 
    236236 
    237         CAMEL_SERVICE_REC_LOCK (store, connect_lock); 
     237        camel_imap_store_stop_idle_connect_lock (store); 
    238238 
    239239        ok = imap_command_start (store, folder, cmd, ex); 
     
    241241 
    242242        if (!ok) 
    243                 CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     243                camel_imap_store_connect_unlock_start_idle (store); 
    244244 
    245245        return ok; 
     
    251251{ 
    252252        ssize_t nwritten; 
     253        ssize_t nread; 
    253254        gchar *resp = NULL; 
    254255        CamelException myex = CAMEL_EXCEPTION_INITIALISER; 
     
    355356 
    356357        /* Read away whatever we got */ 
    357         while (camel_imap_store_readline_nb (store, &resp, &myex) > 0) 
     358        while ((nread = camel_imap_store_readline_nb (store, &resp, &myex)) > 0) 
    358359        { 
     360#ifdef IMAP_DEBUG 
     361                gchar *debug_resp; 
     362                gchar *debug_resp_escaped; 
    359363                imap_debug ("unsolitcited: "); 
    360                 imap_debug (resp); 
     364                debug_resp = g_strndup (resp, nread); 
     365                debug_resp_escaped = g_strescape (debug_resp, ""); 
     366                g_free (debug_resp); 
     367                imap_debug (debug_resp_escaped); 
     368                g_free (debug_resp_escaped); 
    361369                imap_debug ("\n"); 
     370#endif 
    362371 
    363372                g_free (resp); 
     
    435444                        camel_imap_recon (store, &mex, TRUE); 
    436445                        imap_debug ("Recon in cont: %s\n", camel_exception_get_description (&mex)); 
    437                         CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     446                        camel_imap_store_connect_unlock_start_idle (store); 
    438447                        camel_exception_clear (&mex); 
    439448                        return NULL; 
     
    443452                camel_service_disconnect (CAMEL_SERVICE (store), FALSE, NULL); 
    444453 
    445                 CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     454                camel_imap_store_connect_unlock_start_idle (store); 
    446455                return NULL; 
    447456        } 
     
    475484 
    476485        if (camel_imap_store_readline (store, &respbuf, ex) < 0) { 
    477                 CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     486                camel_imap_store_connect_unlock_start_idle (store); 
    478487                return CAMEL_IMAP_RESPONSE_ERROR; 
    479488        } 
     
    538547        if (type == CAMEL_IMAP_RESPONSE_ERROR || 
    539548            type == CAMEL_IMAP_RESPONSE_TAGGED) 
    540                 CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     549                camel_imap_store_connect_unlock_start_idle (store); 
    541550 
    542551        return type; 
     
    623632         */ 
    624633 
    625         CAMEL_SERVICE_REC_LOCK (store, connect_lock); 
     634        camel_imap_store_stop_idle_connect_lock (store); 
    626635 
    627636        response = g_new0 (CamelImapResponse, 1); 
     
    10701079        g_free (response); 
    10711080 
    1072         CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     1081        camel_imap_store_connect_unlock_start_idle (store); 
    10731082} 
    10741083 
  • releases/modest/diablo-pe2/libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.c

    r3799 r3801  
    913913         * should do it.  */ 
    914914        CAMEL_FOLDER_REC_LOCK(folder, lock); 
    915         CAMEL_SERVICE_REC_LOCK (imap_store, connect_lock); 
     915        camel_imap_store_stop_idle_connect_lock (imap_store); 
    916916 
    917917        if (!camel_disco_store_check_online ((CamelDiscoStore*)imap_store, ex)) 
     
    958958        } 
    959959done: 
    960         CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock); 
    961         CAMEL_FOLDER_REC_UNLOCK(folder, lock); 
    962  
    963960        camel_folder_summary_save(folder->summary, ex); 
    964961        camel_store_summary_save((CamelStoreSummary *)((CamelImapStore *)folder->parent_store)->summary, ex); 
    965962 
    966         camel_imap_folder_start_idle (folder); 
     963        camel_imap_store_connect_unlock_start_idle (imap_store); 
     964        CAMEL_FOLDER_REC_UNLOCK(folder, lock); 
    967965 
    968966} 
     
    14981496 
    14991497        camel_exception_init (&local_ex); 
    1500         CAMEL_SERVICE_REC_LOCK (store, connect_lock); 
     1498        camel_imap_store_stop_idle_connect_lock (store); 
    15011499 
    15021500        /* Find a message with changed flags, find all of the other 
     
    15641562                g_ptr_array_free (matches, TRUE); 
    15651563 
    1566                 /* We unlock here so that other threads can have a chance to grab the connect_lock */ 
    1567                 CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
    1568  
    15691564                /* check for an exception */ 
    15701565                if (camel_exception_is_set (&local_ex)) { 
    15711566 
     1567                        camel_imap_store_connect_unlock_start_idle (store); 
     1568 
    15721569                        camel_exception_xfer (ex, &local_ex); 
    1573                         camel_imap_folder_start_idle (folder); 
    15741570                        return; 
    15751571                } 
    15761572 
    1577                 /* Re-lock the connect_lock */ 
    1578                 CAMEL_SERVICE_REC_LOCK (store, connect_lock); 
    15791573        } 
    15801574 
     
    15821576        imap_sync_offline (folder, ex); 
    15831577 
    1584         CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     1578        camel_imap_store_connect_unlock_start_idle (store); 
    15851579        camel_imap_folder_start_idle (folder); 
    15861580 
     
    16361630        char *set; 
    16371631 
    1638         CAMEL_SERVICE_REC_LOCK (store, connect_lock); 
     1632        camel_imap_store_stop_idle_connect_lock (store); 
    16391633 
    16401634        if ((store->capabilities & IMAP_CAPABILITY_UIDPLUS) == 0) { 
    16411635                ((CamelFolderClass *)CAMEL_OBJECT_GET_CLASS(folder))->sync(folder, 0, ex); 
    16421636                if (camel_exception_is_set(ex)) { 
    1643                         CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     1637                        camel_imap_store_connect_unlock_start_idle (store); 
    16441638                        return; 
    16451639                } 
     
    16561650                        camel_imap_response_free (store, response); 
    16571651                if (camel_exception_is_set (ex)) { 
    1658                         CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     1652                        camel_imap_store_connect_unlock_start_idle (store); 
    16591653                        g_free (set); 
    16601654                        return; 
     
    16831677        } 
    16841678 
    1685         CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
    1686  
    1687         camel_imap_folder_start_idle (folder); 
    1688  
     1679        camel_imap_store_connect_unlock_start_idle (store); 
    16891680} 
    16901681 
     
    17111702         * marked un-deleted. */ 
    17121703 
    1713         CAMEL_SERVICE_REC_LOCK (store, connect_lock); 
     1704        camel_imap_store_stop_idle_connect_lock (store); 
    17141705 
    17151706        ((CamelFolderClass *)CAMEL_OBJECT_GET_CLASS(folder))->sync(folder, 0, ex); 
    17161707        if (camel_exception_is_set(ex)) { 
    1717                 CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     1708                camel_imap_store_connect_unlock_start_idle (store); 
    17181709                return; 
    17191710        } 
     
    17261717        result = camel_imap_response_extract (store, response, "SEARCH", ex); 
    17271718        if (!result) { 
    1728                 CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     1719                camel_imap_store_connect_unlock_start_idle (store); 
    17291720                return; 
    17301721        } 
     
    17861777                                g_ptr_array_free (keep_uids, TRUE); 
    17871778                                g_ptr_array_free (mark_uids, TRUE); 
    1788                                 CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     1779                                camel_imap_store_connect_unlock_start_idle (store); 
    17891780                                return; 
    17901781                        } 
     
    18111802                                g_ptr_array_free (keep_uids, TRUE); 
    18121803                                g_ptr_array_free (mark_uids, TRUE); 
     1804                                camel_imap_store_connect_unlock_start_idle (store); 
    18131805                                CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
    18141806                                return; 
     
    18501842        g_free (result); 
    18511843 
    1852         CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
    1853  
    1854         camel_imap_folder_start_idle (folder); 
     1844        camel_imap_store_connect_unlock_start_idle (store); 
    18551845 
    18561846} 
     
    20432033 
    20442034        /* Make sure a "folder_changed" is emitted. */ 
    2045         CAMEL_SERVICE_REC_LOCK (store, connect_lock); 
     2035        camel_imap_store_stop_idle_connect_lock (store); 
    20462036        if (store->current_folder != folder || 
    20472037            camel_folder_summary_count (folder->summary) == count) 
    20482038                imap_refresh_info (folder, ex); 
    2049         CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
    2050  
    2051         camel_imap_folder_start_idle (folder); 
     2039        camel_imap_store_connect_unlock_start_idle (store); 
    20522040 
    20532041} 
     
    21052093         * to copy messages in the other direction from another thread. 
    21062094         */ 
    2107         CAMEL_SERVICE_REC_LOCK (store, connect_lock); 
     2095        camel_imap_store_stop_idle_connect_lock (store); 
    21082096        CAMEL_IMAP_FOLDER_REC_LOCK (source, cache_lock); 
    21092097        CAMEL_IMAP_FOLDER_REC_LOCK (dest, cache_lock); 
    2110         CAMEL_SERVICE_REC_UNLOCK (store, connect_lock); 
     2098        camel_imap_store_connect_unlock_start_idle (store); 
    21112099 
    21122100        if (transferred_uids) { 
     
    28122800                                int i; 
    28132801 
    2814                                 CAMEL_SERVICE_REC_LOCK(store, connect_lock); 
     2802                                camel_imap_store_stop_idle_connect_lock (store); 
    28152803                                if (!camel_disco_store_check_online ((CamelDiscoStore*)store, ex)) { 
    2816                                         CAMEL_SERVICE_REC_UNLOCK(store, connect_lock); 
     2804                                        camel_imap_store_connect_unlock_start_idle (store); 
    28172805                                        camel_exception_set (ex, CAMEL_EXCEPTION_FOLDER_UID_NOT_AVAILABLE, 
    28182806                                                             _("This message is not currently available")); 
     
    28212809 
    28222810                                response = camel_imap_command (store, folder, ex, "UID FETCH %s BODY", uid); 
    2823                                 CAMEL_SERVICE_REC_UNLOCK(store, connect_lock); 
     2811                                camel_imap_store_connect_unlock_start_idle (store); 
    28242812 
    28252813                                if (response) { 
     
    33693357                if (tcnt < (exists - seq)) 
    33703358                { 
    3371                         int i; 
    33723359 
    33733360                        g_ptr_array_foreach (needheaders, (GFunc)g_free, NULL); 
     
    38783865} 
    38793866 
     3867static void 
     3868process_idle_body (CamelImapStore *store, CamelFolder *folder, IdleResponse **idle_response, CamelException *exception) 
     3869{ 
     3870        char *resp = NULL; 
     3871 
     3872        while (camel_imap_store_readline_nb (store, &resp, exception) > 0) { 
     3873                if (resp && strlen (resp) > 1 && resp[0] == '*') { 
     3874                        if (!*idle_response) 
     3875                                *idle_response = idle_response_new (folder); 
     3876                        consume_idle_line (store, folder, resp, *idle_response); 
     3877                } 
     3878                 
     3879                if (resp) 
     3880                        g_free (resp); 
     3881                resp = NULL; 
     3882        } 
     3883} 
     3884 
     3885static void 
     3886process_idle_untagged_response (CamelImapStore *store, CamelFolder *folder, IdleResponse **idle_response, CamelException *exception) 
     3887{ 
     3888        char *resp = NULL; 
     3889        CamelImapResponseType type; 
     3890 
     3891        while ((type = camel_imap_command_response_idle (store, &resp, exception)) == CAMEL_IMAP_RESPONSE_UNTAGGED) { 
     3892                if (resp && strlen (resp) > 1 && resp[0] == '*') { 
     3893                        if (!*idle_response) 
     3894                                *idle_response = idle_response_new (folder); 
     3895                        consume_idle_line (store, folder, resp, *idle_response); 
     3896                } 
     3897                 
     3898                if (resp) 
     3899                        g_free (resp); 
     3900                resp = NULL; 
     3901        } 
     3902} 
     3903 
     3904static void 
     3905do_send_done (CamelImapStore *store, CamelFolder *folder, IdleResponse **idle_resp, CamelException *ex) 
     3906{ 
     3907        if (store->idle_prefix) { 
     3908                gboolean l = g_static_rec_mutex_trylock (store->idle_lock); 
     3909 
     3910                if (!store->idle_kill) { 
     3911                        int nwritten=0; 
     3912                        /* We read-away everything that we still 
     3913                         * have. To find where idle_resp is handled, 
     3914                         * read below at the g_thread_join for 
     3915                         * this thread (we are returning it). */ 
     3916 
     3917                        process_idle_body (store, folder, idle_resp, ex); 
     3918 
     3919                        /* We send the DONE to the server */ 
     3920                         
     3921                        nwritten = camel_stream_printf (store->ostream, "DONE\r\n"); 
     3922                        idle_debug ("(%d, 8) -> DONE\n", nwritten); 
     3923 
     3924                        /* We read away everything the server sends 
     3925                         * until the we see the untagged OK response */ 
     3926 
     3927                        process_idle_untagged_response (store, folder, idle_resp, ex); 
     3928                } 
     3929 
     3930                if (l) 
     3931                        g_static_rec_mutex_unlock (store->idle_lock); 
     3932 
     3933                /* If we are continuing the loop, handle idle_resp 
     3934                 * now (this can invoke fetching new headers). */ 
     3935 
     3936                if (store->idle_cont && *idle_resp) { 
     3937                        process_idle_response (*idle_resp); 
     3938                        idle_response_free (*idle_resp); 
     3939                        *idle_resp = NULL; 
     3940                } 
     3941 
     3942        } 
     3943        store->idle_send_done_happened = TRUE; 
     3944} 
     3945 
     3946static gpointer 
     3947send_done_in_stop_idle (CamelImapStore *store, CamelFolder *folder) 
     3948{ 
     3949        CamelException ex = CAMEL_EXCEPTION_INITIALISER; 
     3950        IdleResponse *idle_resp = NULL; 
     3951 
     3952        /* This method is used for sending the commands we expect idle thread to do to finish 
     3953         * idle loop, when the caller thread has the connect lock (and then the idle thread cannot 
     3954         * do safely these commands */ 
     3955 
     3956        if (g_static_rec_mutex_trylock (store->idle_lock)) { 
     3957                        /* Step B) (see idle_thread) */ 
     3958 
     3959                        if (!store->idle_kill) { 
     3960                                process_idle_body (store, folder, &idle_resp, &ex); 
     3961                        } 
     3962                        g_static_rec_mutex_unlock (store->idle_lock); 
     3963        } 
     3964 
     3965        /* Step C) (see idle_thread). We're assuming idle_cont == FALSE, send_done == TRUE*/ 
     3966        do_send_done (store, folder, &idle_resp, &ex); 
     3967        return idle_resp; 
     3968} 
     3969 
    38803970typedef struct { 
    38813971        CamelFolder *folder; 
     
    38943984        gboolean tfirst = TRUE, first = TRUE, my_cont, had_cond = FALSE; 
    38953985        int cnt = 0; 
    3896         int nwritten=0; 
    38973986        gpointer retval = NULL; 
    38983987 
     
    39574046        } 
    39584047 
     4048        store->idle_send_done_happened = FALSE; 
    39594049        /* While nothing has stopped us yet ... 
    39604050         * TNY TODO: it would be nicer to use select() here, rather than usleep() */ 
    3961  
    3962         while (my_cont && !store->idle_kill) 
     4051         
     4052        while (my_cont && !store->idle_kill &&  
     4053               (store->idle_cont || !store->idle_send_done_happened)) 
    39634054        { 
    39644055                CamelException ex = CAMEL_EXCEPTION_INITIALISER; 
    3965                 char *resp = NULL; 
    39664056                IdleResponse *idle_resp = NULL; 
    39674057                gboolean senddone = FALSE; 
    3968  
    3969                 /* A) The first time we will start the IDLE by sending IDLE to 
    3970                  * the server and reading away the + continuation (check out the 
    3971                  * idle_real_start function). We don't call this in the other 
    3972                  * loop cycles of this while. */ 
    3973  
    3974                 if (store->idle_cont && first) { 
    3975                         gboolean l = g_static_rec_mutex_trylock (store->idle_lock); 
    3976                         if (!store->idle_kill) 
    3977                                 idle_real_start (store); 
    3978                         if (l) 
    3979                                 g_static_rec_mutex_unlock (store->idle_lock); 
    3980                         first = FALSE; 
    3981                 } 
    3982  
    3983                 /* And we also send the broadcast to the caller of this thread: 
    3984                  * We're started, and we're fine. It can continue. We don't call 
    3985                  * this in the other loop cycles of this while. */ 
    3986  
    3987                 if (tfirst) { 
    3988                         if (info->condition) { 
    3989                                 g_mutex_lock (info->mutex); 
    3990                                 g_cond_broadcast (info->condition); 
    3991                                 info->had_cond = TRUE; had_cond = TRUE; 
    3992                                 g_mutex_unlock (info->mutex); 
    3993                         } 
    3994                         tfirst = FALSE; 
    3995                 } 
    3996  
    3997                 if (g_static_rec_mutex_trylock (store->idle_lock)) 
    3998                 { 
    3999                         /* B) This happens during the IDLE's body (after IDLE is 
    4000                          * started and before DONE is sent). We read away the 
    4001                          * lines in a non-blocking way. As soon as we have a 
    4002                          * full line, that starts with '*', we consume it. */ 
    4003  
    4004                         if (!store->idle_kill) { 
    4005                                 while (camel_imap_store_readline_nb (store, &resp, &ex) > 0) 
    4006                                 { 
    4007                                         if (resp && strlen (resp) > 1 && resp[0] == '*') { 
    4008                                                 if (!idle_resp) 
    4009                                                         idle_resp = idle_response_new (folder); 
    4010                                                 consume_idle_line (store, folder, resp, idle_resp); 
    4011                                         } 
    4012  
    4013                                         if (resp) 
    4014                                                 g_free (resp); 
    4015                                         resp = NULL; 
    4016                                 } 
    4017                         } 
    4018                         g_static_rec_mutex_unlock (store->idle_lock); 
    4019                 } 
    4020  
    4021                 if (resp) 
    4022                         g_free (resp); 
    4023  
    4024                 if (store->idle_cont) 
    4025                 { 
    4026                         if (idle_resp && !idle_resp->exists_happened) { 
    4027                                 /* We can process it already: nothing is at this moment 
    4028                                  * joining us, nothing is at this moment locking the 
    4029                                  * folder_changed handler of TnyCamelFolder */ 
    4030                                 process_idle_response (idle_resp); 
    4031                                 idle_response_free (idle_resp); 
    4032                                 idle_resp = NULL; 
    4033                                 retval = NULL; 
    4034                         } else if (idle_resp && idle_resp->exists_happened) { 
    4035  
    4036                                 /* We can't deal with new EXISTS responses 
    4037                                  * without first stopping IDLE (we'll need to 
    4038                                  * fetch the new headers) */ 
    4039  
    4040                                 senddone = TRUE; 
    4041                                 retval = idle_resp; 
    4042                         } 
    4043                 } else { 
    4044                         /* If store->idle_cont was FALSE, we're going to handle 
    4045                          * idle_resp differently (look below). */ 
    4046                         senddone = TRUE; 
    4047                         retval = idle_resp; 
    4048                 } 
    4049  
    4050                 /* C) So either we timed out (store->idle_sleep as been reached), 
    4051                  * which means that we 'are' going to restart this entire while, 
    4052                  * including resending the IDLE-start, after we're done with 
    4053                  * this if-block of course. 
    4054                  * 
    4055                  * Or another thread called us to stop IDLE, and then we're 
    4056                  * going to exit this while, of course. If it was an idle_kill, 
    4057                  * we're not even going to try doing that in a nice way. In that 
    4058                  * case we'll just exit ASAP (it's let_idle_die in CamelImapStore 
    4059                  * trying to disconnect from the IMAP server). */ 
    4060  
    4061                 if ((cnt > store->idle_sleep) || senddone) 
    4062                 { 
    4063                         if (store->idle_prefix) 
    4064                         { 
    4065                                 CamelImapResponseType type; 
     4058                 
     4059                if (CAMEL_SERVICE_REC_TRYLOCK (store, connect_lock)) { 
     4060                        /* A) The first time we will start the IDLE by sending IDLE to 
     4061                         * the server and reading away the + continuation (check out the 
     4062                         * idle_real_start function). We don't call this in the other 
     4063                         * loop cycles of this while. */ 
     4064 
     4065                        if (store->idle_cont && first) { 
    40664066                                gboolean l = g_static_rec_mutex_trylock (store->idle_lock); 
    4067  
    40684067                                if (!store->idle_kill) 
    4069                                 { 
    4070                                         /* We read-away everything that we still 
    4071                                          * have. To find where idle_resp is handled, 
    4072                                          * read below at the g_thread_join for 
    4073                                          * this thread (we are returning it). */ 
    4074  
    4075                                         resp = NULL; 
    4076                                         while (camel_imap_store_readline_nb (store, &resp, &ex) > 0) 
    4077                                         { 
    4078                                                 if (resp && strlen (resp) > 1 && resp[0] == '*') { 
    4079                                                         if (!idle_resp) { 
    4080                                                                 idle_resp = idle_response_new (folder); 
    4081                                                                 /* We will free this after the join */ 
    4082                                                                 if (!store->idle_cont) 
    4083                                                                         retval = idle_resp; 
    4084                                                         } 
    4085                                                         consume_idle_line (store, folder, resp, idle_resp); 
    4086                                                 } 
    4087  
    4088                                                 if (resp) 
    4089                                                         g_free (resp); 
    4090                                                 resp = NULL; 
    4091                                         } 
    4092                                         if (resp) 
    4093                                                 g_free (resp); 
    4094                                         resp = NULL; 
    4095  
    4096                                         /* We send the DONE to the server */ 
    4097  
    4098                                         nwritten = camel_stream_printf (store->ostream, "DONE\r\n"); 
    4099                                         idle_debug ("(%d, 8) -> DONE\n", nwritten); 
    4100  
    4101                                         /* We read away everything the server sends 
    4102                                          * until the we see the untagged OK response */ 
    4103  
    4104                                         while ((type = camel_imap_command_response_idle (store, &resp, &ex)) == CAMEL_IMAP_RESPONSE_UNTAGGED) 
    4105                                         { 
    4106                                                 if (resp && strlen (resp) > 1 && resp[0] == '*') { 
    4107                                                         if (!idle_resp) { 
    4108                                                                 idle_resp = idle_response_new (folder); 
    4109                                                                 /* We will free this after the join */ 
    4110                                                                 if (!store->idle_cont) 
    4111                                                                         retval = idle_resp; 
    4112                                                         } 
    4113                                                         consume_idle_line (store, folder, resp, idle_resp); 
    4114                                                 } 
    4115  
    4116                                                 if (resp) 
    4117                                                         g_free (resp); 
    4118                                                 resp = NULL; 
    4119                                         } 
    4120                                 } 
    4121  
     4068                                        idle_real_start (store); 
    41224069                                if (l) 
    41234070                                        g_static_rec_mutex_unlock (store->idle_lock); 
    4124  
    4125                                 if (resp) 
    4126                                         g_free (resp); 
    4127                                 resp = NULL; 
    4128  
    4129                                 /* If we are continuing the loop, handle idle_resp 
    4130                                  * now (this can invoke fetching new headers). */ 
    4131  
    4132                                 if (store->idle_cont && idle_resp) { 
     4071                                first = FALSE; 
     4072                        } 
     4073 
     4074                        /* And we also send the broadcast to the caller of this thread: 
     4075                         * We're started, and we're fine. It can continue. We don't call 
     4076                         * this in the other loop cycles of this while. */ 
     4077                         
     4078                        if (tfirst) { 
     4079                                if (info->condition) { 
     4080                                        g_mutex_lock (info->mutex); 
     4081                                        g_cond_broadcast (info->condition); 
     4082                                        info->had_cond = TRUE; had_cond = TRUE; 
     4083                                        g_mutex_unlock (info->mutex); 
     4084                                } 
     4085                                tfirst = FALSE; 
     4086                        } 
     4087 
     4088                        if (g_static_rec_mutex_trylock (store->idle_lock)) { 
     4089                                /* B) This happens during the IDLE's body (after IDLE is 
     4090                                 * started and before DONE is sent). We read away the 
     4091                                 * lines in a non-blocking way. As soon as we have a 
     4092                                 * full line, that starts with '*', we consume it. */ 
     4093                                 
     4094                                if (!store->idle_kill) { 
     4095                                        process_idle_body (store, folder, &idle_resp, &ex); 
     4096                                } 
     4097                                g_static_rec_mutex_unlock (store->idle_lock); 
     4098                        } 
     4099                         
     4100                        if (store->idle_cont) { 
     4101                                if (idle_resp && !idle_resp->exists_happened) { 
     4102                                        /* We can process it already: nothing is at this moment 
     4103                                         * joining us, nothing is at this moment locking the 
     4104                                         * folder_changed handler of TnyCamelFolder */ 
    41334105                                        process_idle_response (idle_resp); 
    41344106                                        idle_response_free (idle_resp); 
    41354107                                        idle_resp = NULL; 
    41364108                                        retval = NULL; 
     4109                                } else if (idle_resp && idle_resp->exists_happened) { 
     4110 
     4111                                        /* We can't deal with new EXISTS responses 
     4112                                         * without first stopping IDLE (we'll need to 
     4113                                         * fetch the new headers) */ 
     4114                                         
     4115                                        senddone = TRUE; 
     4116                                        retval = idle_resp; 
    41374117                                } 
    4138                         } 
    4139  
    4140                         if (store->idle_cont) 
    4141                                 first = TRUE; 
    4142                         else 
    4143                                 my_cont = FALSE; 
     4118                        } else { 
     4119                                /* If store->idle_cont was FALSE, we're going to handle 
     4120                                 * idle_resp differently (look below). */ 
     4121                                senddone = TRUE; 
     4122                                retval = idle_resp; 
     4123                        } 
     4124 
     4125                        /* C) So either we timed out (store->idle_sleep as been reached), 
     4126                         * which means that we 'are' going to restart this entire while, 
     4127                         * including resending the IDLE-start, after we're done with 
     4128                         * this if-block of course. 
     4129                         * 
     4130                         * Or another thread called us to stop IDLE, and then we're 
     4131                         * going to exit this while, of course. If it was an idle_kill, 
     4132                         * we're not even going to try doing that in a nice way. In that 
     4133