View | Details | Raw Unified | Return to bug 51019
Collapse All | Expand All

(-)httpd-2.2.17_orig/NOTICE (+3 lines)
Lines 18-20 Link Here
18
by the University of Cambridge, England. The original software is
18
by the University of Cambridge, England. The original software is
19
available from
19
available from
20
   ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
20
   ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
21
22
Portions of this software were developed by Symantec Corporation at 
23
Mountain View, California.
(-)httpd-2.2.17_orig/docs/conf/extra/httpd-mpm.conf.in (-1 / +52 lines)
Lines 106-110 Link Here
106
<IfModule mpm_winnt_module>
106
<IfModule mpm_winnt_module>
107
    ThreadsPerChild      150
107
    ThreadsPerChild      150
108
    MaxRequestsPerChild    0
108
    MaxRequestsPerChild    0
109
</IfModule>
110
109
110
# Asynchronous Send File configuration (for WinNT+ only)
111
#
112
#   1. Win32 AcceptEx should not be disabled.
113
#
114
#   2. EnableSendFile should be on.
115
#
116
#   3. AsyncSendFile <anydirectory|givendirectory|off>
117
#                          |            |          +--->Disable AsyncFileWrite,
118
#                          |            |               default is off
119
#                          |            |
120
#                          |            +--> Force AsyncFileWrite only for folders
121
#                          |                 specified by <ForceAsyncSendFile>
122
#                          |                 config item (refer below)
123
#                          |
124
#                          +--> Apache does AsyncFileWrite for files greater
125
#                               than 10KB in size, or greater than the size 
126
#                               specified by <AsyncSendFileMinSize> config item
127
#
128
#   4. AsyncSendFileMinSize <size in Bytes>
129
#                                  +---> Default is 1MB, any value less than 1MB 
130
#                                        given will be discarded and set to default.
131
EnableSendFile on
132
AsyncSendFile anydirectory
133
134
# 1MB limit is put to filter PHP/jscript files so that it gets delivered quickly
135
AsyncSendFileMinSize	1048576
136
137
#
138
# To force Async File Send only for specific folders
139
#	AsyncSendFile givendirectory
140
#
141
#ForceAsyncSendFile "C:/Program Files/Symantec/Symantec Endpoint Protection Manager/Inetpub/content"
142
143
144
#
145
# Max number of connection requests that will be queued in the child httpd process
146
#    ConnectionsToQueuePerChild <number of connections>
147
#                                  +--> Must be greater than ThreadsPerChild item.
148
#                                            Default is 5000.
149
ConnectionsToQueuePerChild 1000
150
151
152
#
153
# To dedicate worker threads specifically for Async Callback processing
154
#    DedicatedThreadsForAsyncCallbacks <number of threads>
155
#					                     +--> Must be greater than half the
156
#						                      ThreadsPerChild config item,
157
#                                             Default is 0.
158
#
159
#DedicatedThreadsForAsyncCallbacks 1
160
161
</IfModule>
(-)httpd-2.2.17_orig/docs/manual/mod/mpm_winnt.xml (+136 lines)
Lines 88-91 Link Here
88
</usage>
88
</usage>
89
</directivesynopsis>
89
</directivesynopsis>
90
90
91
92
<directivesynopsis>
93
<name>ConnectionsToQueuePerChild</name>
94
<description>Number of connections to queue</description>  
95
    <syntax>
96
      ConnectionsToQueuePerChild <var>number</var>
97
    </syntax>
98
    <default>ConnectionsToQueuePerChild 5000</default>
99
    <contextlist>
100
      <context>server config</context>
101
    </contextlist>
102
    <compatibility>Available in Version 2.2.17 and later</compatibility>
103
    <modulelist>
104
      <module>mpmwinnt</module>
105
    </modulelist>
106
107
    <usage>
108
      <p>
109
        The <directive>ConnectionsToQueuePerChild</directive> directive sets the 
110
        limit on the number of simultaneous requests that can be queued if all
111
        the worker threads are busy. The <directive>ConnectionsToQueuePerChild</directive> 
112
        cannot not be set to less than <directive>ThreadsPerChild</directive>        
113
      </p>
114
115
      <example>
116
        ConnectionsToQueuePerChild 1000
117
    </example>
118
119
    </usage>
120
</directivesynopsis>
121
122
123
<directivesynopsis>
124
<name>AsyncSendFile</name>
125
<description>Enable non-blocking asynchronous file transmission</description>  
126
    <syntax>
127
      AsyncSendFile Off| <var> AnyDirectory|GivenDirectory </var>    
128
    </syntax>
129
    <default>AsyncSendFile Off</default>
130
    <contextlist>
131
      <context>server config</context>
132
    </contextlist>
133
    <compatibility>Available in mpm_winnt version 2.2.17 and later</compatibility>
134
    <modulelist>
135
      <module>mpmwinnt</module>
136
    </modulelist>
137
    <usage>
138
      <p>
139
        The <directive>AsyncSendFile</directive> directive enables file download 
140
        requests to be completely handled by kernel asynchronously thereby freeing 
141
        up Apache worker threads to do other tasks. The <directive>AsyncSendFile</directive> 
142
        will work only if <directive>EnableSendFile</directive> is enabled, and 
143
        <directive>Win32DisableAcceptEx</directive> should be disabled. 
144
      </p>
145
146
      <p>
147
        If this directive is set to <code>AnyDirectory</code>, then file download
148
        requests will be automatically determined to be transmitted asynchronously or 
149
        not based on the <directive>AsyncSendFileMinSize</directive> diective.
150
      </p>
151
152
      <p>
153
        If this directive is set to <code>GivenDirectory</code>, then file download
154
        requests containting URI paths mentioned by <directive>ForceAsyncSendFile</directive>
155
        will be transmitted asynchronously.
156
      </p>
157
158
      <example>
159
        AsyncSendFile AnyDirectory<br />                
160
    </example>
161
162
    </usage>
163
</directivesynopsis>
164
165
166
<directivesynopsis>
167
<name>AsyncSendFileMinSize</name>
168
<description>Denotes when non-blocking asynchronous transmission should be triggered</description>  
169
    <syntax>
170
      AsyncSendFileMinSize <var>Size in bytes</var>    
171
    </syntax>
172
    <default>AsyncSendFileMinSize 1048576</default>
173
    <contextlist>
174
      <context>server config</context>
175
    </contextlist>
176
    <compatibility>Available in mpm_winnt version 2.2.17 and later</compatibility>
177
    <modulelist>
178
      <module>mpmwinnt</module>
179
    </modulelist>
180
    <usage>
181
      <p>
182
        The <directive>AsyncSendFileMinSize</directive> directive specifies the minimum
183
        file size in bytes for it to be transmitted asynchronously. Refer
184
        <directive>AsyncSendFile</directive> directive for more information.
185
      </p>
186
        
187
    <example>
188
      AsyncSendFile AnyDirectory <br />        
189
      AsyncSendFileMinSize  485760
190
    </example>
191
192
    </usage>
193
</directivesynopsis>
194
195
196
      
197
<directpivesynopsis>
198
<name>ForceAsyncSendFile</name>
199
<description>Forces non-blocking asynchronous transmission for a given folder</description>  
200
    <syntax>
201
      ForceAsyncSendFile <var>Absoluted folder path</var>    
202
    </syntax>
203
    <default>AsyncSendFileMinSize 10485760</default>
204
    <contextlist>
205
      <context>server config</context>
206
    </contextlist>
207
    <compatibility>Available in mpm_winnt version 2.2.17 and later</compatibility>
208
    <modulelist>
209
      <module>mpmwinnt</module>
210
    </modulelist>
211
    <usage>
212
      <p>
213
        The <directive>AsyncSendFileMinSize</directive> directive specifies the minimum
214
        required file size in Bytes for it to be transmitted asynchronously, if size
215
        given is less than the 1MB this directive defaults to 1MB.
216
        <directive>AsyncSendFile</directive>
217
      </p>
218
    
219
    <example>
220
      AsyncSendFile GivenDirectory <br />        
221
      ForceAsyncSendFile  "C:/ApachePub/Downloads/2012"
222
    </example>
223
224
    </usage>
225
</directivesynopsis>
226
91
</modulesynopsis>
227
</modulesynopsis>
(-)httpd-2.2.17_orig/include/http_core.h (+4 lines)
Lines 659-664 Link Here
659
extern AP_DECLARE_DATA ap_filter_rec_t *ap_content_length_filter_handle;
659
extern AP_DECLARE_DATA ap_filter_rec_t *ap_content_length_filter_handle;
660
extern AP_DECLARE_DATA ap_filter_rec_t *ap_core_input_filter_handle;
660
extern AP_DECLARE_DATA ap_filter_rec_t *ap_core_input_filter_handle;
661
661
662
/* Handle for Winnt async filter */
663
#ifdef _WIN32  
664
extern AP_DECLARE_DATA ap_filter_rec_t *ap_core_async_output_filter_handle;
665
#endif /* _WIN32 */
662
/**
666
/**
663
 * This hook provdes a way for modules to provide metrics/statistics about
667
 * This hook provdes a way for modules to provide metrics/statistics about
664
 * their operational status.
668
 * their operational status.
(-)httpd-2.2.17_orig/include/http_log.h (+32 lines)
Lines 352-357 Link Here
352
                       const request_rec *r, apr_pool_t *pool,
352
                       const request_rec *r, apr_pool_t *pool,
353
                       const char *errstr))
353
                       const char *errstr))
354
354
355
     
356
357
/**
358
 * Log Macros for VC2005 and above compilers for optimized execution
359
 */
360
#if _MSC_VER > 1400
361
362
extern server_rec* ap_server_conf;
363
#define  AP_LOG_ERROR(fileline, level, ...)	do{	\
364
         if ((ap_server_conf != NULL) && ((level) <= ap_server_conf->loglevel))\
365
		     ap_log_error(fileline, level, __VA_ARGS__);\
366
    }while(0);
367
368
369
#define  AP_LOG_CERROR(fileline, level, ...) do{ \
370
         if ((ap_server_conf != NULL) && ((level) <= ap_server_conf->loglevel))\
371
             ap_log_cerror(fileline, level, __VA_ARGS__);\
372
    }while(0);
373
374
#define  AP_LOG_RERROR(file, line, level, ...)  do{	\
375
         if ((ap_server_conf != NULL) && ((level) <= ap_server_conf->loglevel))\
376
		      ap_log_rerror(fileline, level, __VA_ARGS__);\
377
    }while(0);
378
379
#else
380
381
#define AP_LOG_ERROR   ap_log_error
382
#define AP_LOG_CERROR  ap_log_cerror
383
#define AP_LOG_RERROR  ap_log_rerror
384
385
#endif /*_MSC_VER > 1400*/
386
355
#ifdef __cplusplus
387
#ifdef __cplusplus
356
}
388
}
357
#endif
389
#endif
(-)httpd-2.2.17_orig/include/http_request.h (-1 / +1 lines)
Lines 240-246 Link Here
240
 * Function called by main.c to handle first-level request 
240
 * Function called by main.c to handle first-level request 
241
 * @param r The current request
241
 * @param r The current request
242
 */
242
 */
243
void ap_process_request(request_rec *);
243
int ap_process_request(request_rec *);
244
244
245
/**
245
/**
246
 * Kill the current request
246
 * Kill the current request
(-)httpd-2.2.17_orig/include/httpd.h (+64 lines)
Lines 470-475 Link Here
470
#define DONE -2			/**< Module has served the response completely 
470
#define DONE -2			/**< Module has served the response completely 
471
				 *  - it's safe to die() with no more output
471
				 *  - it's safe to die() with no more output
472
				 */
472
				 */
473
474
#define PENDING -3      // Represents request processing is not completed yet
475
                        // but reliquish the worker thread for now because
476
                        // an Async I/O may be issued or some background
477
                        // work is going on that is expected to complete
478
                        // after few seconds/minutes or more
479
473
#define OK 0			/**< Module has handled this stage. */
480
#define OK 0			/**< Module has handled this stage. */
474
481
475
482
Lines 1004-1009 Link Here
1004
 * record to improve 64bit alignment the next time we need to break
1011
 * record to improve 64bit alignment the next time we need to break
1005
 * binary compatibility for some other reason.
1012
 * binary compatibility for some other reason.
1006
 */
1013
 */
1014
1015
    /** Store the Isapi Async operational state */
1016
    int nLastAsyncOperation;    
1007
};
1017
};
1008
1018
1009
/**
1019
/**
Lines 1033-1038 Link Here
1033
} ap_conn_keepalive_e;
1043
} ap_conn_keepalive_e;
1034
1044
1035
/** 
1045
/** 
1046
 * @brief Enumeration of connection async state
1047
 */
1048
typedef enum {
1049
    /** Represents there are no Async operations on current connection */
1050
    CONN_OPSTATE_ASYNC_NONE = 0,    
1051
1052
    /* The following states are used for synchronized access of connection
1053
     * context objects between threads*/
1054
1055
    /* Denotes Async request spawned successfully by worker thread but no 
1056
     * other threads should start working on the Async completion notification
1057
     * yet (assuming async operation already completed). This is because the 
1058
     * current worker thread is yet to release the connection context object.     
1059
     */
1060
    CONN_OPSTATE_ASYNC_PRE_READY,   
1061
1062
    /* Denotes the worker thread that spawned Async request has released the
1063
     * connection context, and any thread can start working on the Async 
1064
     * completion notification
1065
     */
1066
    CONN_OPSTATE_ASYNC_READY,
1067
1068
    /* Denotes the Async completion notification is handled, and required 
1069
     * resource cleanup can be done on this connection object
1070
     */
1071
    CONN_OPSTATE_ASYNC_DONE
1072
1073
} conn_operational_state_e;
1074
1075
/** 
1036
 * @brief Structure to store things which are per connection 
1076
 * @brief Structure to store things which are per connection 
1037
 */
1077
 */
1038
struct conn_rec {
1078
struct conn_rec {
Lines 1103-1108 Link Here
1103
     *  the event mpm.
1143
     *  the event mpm.
1104
     */
1144
     */
1105
    int clogging_input_filters;
1145
    int clogging_input_filters;
1146
1147
    /** Contains the Context object's OVERLAPPED reference */
1148
    void*   pOverlapped;            
1149
1150
    /** Holds IOCP handle, will be null if AcceptEx is disabled */
1151
    HANDLE  hAsyncDispatchIOCP;    
1152
1153
    /** Light weight operational state of connection*/
1154
    volatile conn_operational_state_e opState;
1155
1156
    /** Request record*/
1157
    request_rec*   pRequest;
1158
    
1159
    /** Async operation state/flags*/
1160
    struct {        
1161
        unsigned char bIsLinkedToIOCP:1;
1162
        unsigned char bAsyncSendFile:1;
1163
        unsigned char bAsyncSockRead:1;        
1164
    } AsyncOperation;
1165
1166
1167
    /* Below items are for debugging purpose, remove for production*/
1168
    void*   pContext;
1169
    long    nUsageCount;
1106
};
1170
};
1107
1171
1108
/** 
1172
/** 
(-)httpd-2.2.17_orig/modules/arch/win32/mod_isapi.c (-5 / +159 lines)
Lines 52-57 Link Here
52
#include "apr_thread_rwlock.h"
52
#include "apr_thread_rwlock.h"
53
#include "apr_hash.h"
53
#include "apr_hash.h"
54
#include "mod_isapi.h"
54
#include "mod_isapi.h"
55
#include "..\..\..\srclib\apr\include\arch\win32\apr_arch_file_io.h"
56
#include "..\..\..\server\mpm\winnt\mpm_winnt.h"
55
57
56
/* Retry frequency for a failed-to-load isapi .dll */
58
/* Retry frequency for a failed-to-load isapi .dll */
57
#define ISAPI_RETRY apr_time_from_sec(30)
59
#define ISAPI_RETRY apr_time_from_sec(30)
Lines 860-865 Link Here
860
    return (rv == APR_SUCCESS);
862
    return (rv == APR_SUCCESS);
861
}
863
}
862
864
865
/* SetNotes
866
 * Records the last async operation that was invoked
867
 * LTAC: A function to set async opstate may look as overkill here, but I believe 
868
 * this will be helpful for debugging and also in future if we may have to 
869
 * support multiple Async requests on the same connection record? 
870
 */
871
int SetNotes(request_rec* r, int nLastAsyncOp)
872
{
873
    int rv = ERROR_NOT_CAPABLE;
874
875
    if (r){
876
        /*ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
877
            "Thread(%04d) Connection(%05d,%s) SetNotes(LastAsyncOp,%d) ", 
878
            GetCurrentThreadId(), r->connection->id, "", nLastAsyncOp);*/
879
880
        r->nLastAsyncOperation = nLastAsyncOp;        
881
    }
882
883
    return rv ;
884
}
885
886
887
/* GetNotes
888
 * Reads the last async operation that was invoked
889
 */
890
int GetNotes(request_rec* r)
891
{
892
    int nLastAsyncOp = 0;
893
    
894
    if (r){
895
        
896
        nLastAsyncOp = r->nLastAsyncOperation;
897
    }
898
        
899
    /*ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
900
        "Thread(%04d) Connection(%05d,%s) GetNotes(LastAsyncOp,%d) ", 
901
        GetCurrentThreadId(), r->connection->id, "", nLastAsyncOp);*/
902
903
    return nLastAsyncOp;    
904
}
905
863
int APR_THREAD_FUNC ServerSupportFunction(isapi_cid    *cid,
906
int APR_THREAD_FUNC ServerSupportFunction(isapi_cid    *cid,
864
                                          apr_uint32_t  HSE_code,
907
                                          apr_uint32_t  HSE_code,
865
                                          void         *buf_ptr,
908
                                          void         *buf_ptr,
Lines 947-952 Link Here
947
    }
990
    }
948
991
949
    case HSE_REQ_DONE_WITH_SESSION:
992
    case HSE_REQ_DONE_WITH_SESSION:
993
    {   
994
        if (c->hAsyncDispatchIOCP != NULL){        /* LTAC: using IOCP? */
995
            int nLastAsyncOp = 0;
996
997
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
998
                              "Thread(%04d) Connection(%05d,%s) ISAPI: SupportFunc"
999
                              " HSE_REQ_DONE_WITH_SESSION is called", 
1000
                              GetCurrentThreadId(), c->id, r->uri);
1001
            
1002
            nLastAsyncOp = GetNotes(r);
1003
            if (nLastAsyncOp == 0){
1004
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1005
                              "Thread(%04d) Connection(%05d,%s) ISAPI: SupportFunc"
1006
                              " HSE_REQ_DONE_WITH_SESSION is called without prior "
1007
                              "valid HSE_* calls", GetCurrentThreadId(), c->id, 
1008
                              r->uri);
1009
                return 0;
1010
            }
1011
            /* LTAC: For now we support only one Async operation - PUSH */
1012
            else if (nLastAsyncOp == HSE_STATUS_PENDING){
1013
                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1014
                              "Thread(%04d) Connection(%05d,%s) ISAPI: SupportFunc"
1015
                              " HSE_REQ_DONE_WITH_SESSION invoked for prior "
1016
                              " HSE_STATUS_PENDING call, Posting IOCP packet",
1017
                              GetCurrentThreadId(), c->id, r->uri);
1018
1019
                /* Signal to close - connection and destroy memory associated to it
1020
                 */                
1021
                PostQueuedCompletionStatus(c->hAsyncDispatchIOCP, 0, 
1022
                                 IOCP_CONNECTION_PENDING_DONE, c->pOverlapped);
1023
                
1024
                /* !!!!CAUTION CAUTION CAUTION!!!!
1025
                 * Do not set HSE_REQ_DONE_WITH_SESSION as Last async op at this
1026
                 * point since request + contexts objects could have been destroyed 
1027
                 * by now due to the above IOCP packet
1028
                 */
1029
            } 
1030
            else{
1031
                SetNotes(r, HSE_REQ_DONE_WITH_SESSION);
1032
            }
1033
            return 1;
1034
        }
1035
950
        /* Signal to resume the thread completing this request,
1036
        /* Signal to resume the thread completing this request,
951
         * leave it to the pool cleanup to dispose of our mutex.
1037
         * leave it to the pool cleanup to dispose of our mutex.
952
         */
1038
         */
Lines 962-967 Link Here
962
        }
1048
        }
963
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1049
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
964
        return 0;
1050
        return 0;
1051
    }
965
1052
966
    case HSE_REQ_MAP_URL_TO_PATH:
1053
    case HSE_REQ_MAP_URL_TO_PATH:
967
    {
1054
    {
Lines 1029-1034 Link Here
1029
         * Per MS docs... HSE_REQ_IO_COMPLETION replaces any prior call
1116
         * Per MS docs... HSE_REQ_IO_COMPLETION replaces any prior call
1030
         * to HSE_REQ_IO_COMPLETION, and buf_data may be set to NULL.
1117
         * to HSE_REQ_IO_COMPLETION, and buf_data may be set to NULL.
1031
         */
1118
         */
1119
        SetNotes(r, HSE_REQ_IO_COMPLETION);
1032
        if (cid->dconf.fake_async) {
1120
        if (cid->dconf.fake_async) {
1033
            cid->completion = (PFN_HSE_IO_COMPLETION) buf_data;
1121
            cid->completion = (PFN_HSE_IO_COMPLETION) buf_data;
1034
            cid->completion_arg = (void *) data_type;
1122
            cid->completion_arg = (void *) data_type;
Lines 1050-1058 Link Here
1050
        apr_ssize_t ate = 0;
1138
        apr_ssize_t ate = 0;
1051
        apr_bucket_brigade *bb;
1139
        apr_bucket_brigade *bb;
1052
        apr_bucket *b;
1140
        apr_bucket *b;
1053
        apr_file_t *fd;
1141
        apr_file_t *fd = NULL;
1054
        apr_off_t fsize;
1142
        apr_off_t fsize;
1055
1143
1144
        SetNotes(r, HSE_REQ_TRANSMIT_FILE);
1056
        if (!cid->dconf.fake_async && (tf->dwFlags & HSE_IO_ASYNC)) {
1145
        if (!cid->dconf.fake_async && (tf->dwFlags & HSE_IO_ASYNC)) {
1057
            if (cid->dconf.log_unsupported)
1146
            if (cid->dconf.log_unsupported)
1058
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1147
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
Lines 1064-1074 Link Here
1064
1153
1065
        /* Presume the handle was opened with the CORRECT semantics
1154
        /* Presume the handle was opened with the CORRECT semantics
1066
         * for TransmitFile
1155
         * for TransmitFile
1156
             * Adding send file flag to use Transmit file, however this will 
1157
             * not result in true async mode/callbacks - since EOC bucket
1158
             * is not added to brigade before passing down to output filters.
1067
         */
1159
         */
1068
        if ((rv = apr_os_file_put(&fd, &tf->hFile,
1160
        if ((rv = apr_os_file_put(&fd, &tf->hFile,
1161
                        (c->hAsyncDispatchIOCP ? APR_SENDFILE_ENABLED : 0) |
1069
                                  APR_READ | APR_XTHREAD, r->pool))
1162
                                  APR_READ | APR_XTHREAD, r->pool))
1070
                != APR_SUCCESS) {
1163
                != APR_SUCCESS) {
1071
            return 0;
1164
                goto cleanup;	
1072
        }
1165
        }
1073
        if (tf->BytesToWrite) {
1166
        if (tf->BytesToWrite) {
1074
            fsize = tf->BytesToWrite;
1167
            fsize = tf->BytesToWrite;
Lines 1143-1149 Link Here
1143
        /* Use tf->pfnHseIO + tf->pContext, or if NULL, then use cid->fnIOComplete
1236
        /* Use tf->pfnHseIO + tf->pContext, or if NULL, then use cid->fnIOComplete
1144
         * pass pContect to the HseIO callback.
1237
         * pass pContect to the HseIO callback.
1145
         */
1238
         */
1146
        if (tf->dwFlags & HSE_IO_ASYNC) {
1239
        if (rv == APR_SUCCESS &&        /* LTAC: Invoke callback only in case of success */
1240
            tf->dwFlags & HSE_IO_ASYNC) {
1147
            if (tf->pfnHseIO) {
1241
            if (tf->pfnHseIO) {
1148
                if (rv == APR_SUCCESS) {
1242
                if (rv == APR_SUCCESS) {
1149
                    tf->pfnHseIO(cid->ecb, tf->pContext,
1243
                    tf->pfnHseIO(cid->ecb, tf->pContext,
Lines 1165-1170 Link Here
1165
                }
1259
                }
1166
            }
1260
            }
1167
        }
1261
        }
1262
1263
cleanup:
1264
1265
		/* LTAC: By now the Event is used, we can safely clean it 
1266
		 * Note: file_cleanup function closes the event handle, but it gets trigerred only during apr_pool destruction, 
1267
		 * and even though mod_isapi.so creates file handle at Request pool level, the destruction is not happening once request 
1268
		 * processing is done, thereby resulting in event handle leak. 
1269
         */
1270
		if (fd != NULL &&
1271
			fd->pOverlapped != NULL &&
1272
			fd->pOverlapped->hEvent != NULL &&
1273
			fd->pOverlapped->hEvent != INVALID_HANDLE_VALUE){	
1274
				CloseHandle(fd->pOverlapped->hEvent);
1275
				fd->pOverlapped->hEvent = NULL;
1276
		}
1277
1278
1168
        return (rv == APR_SUCCESS);
1279
        return (rv == APR_SUCCESS);
1169
    }
1280
    }
1170
1281
Lines 1185-1190 Link Here
1185
    {
1296
    {
1186
        apr_uint32_t read = 0;
1297
        apr_uint32_t read = 0;
1187
        int res;
1298
        int res;
1299
1300
        SetNotes(r, HSE_REQ_ASYNC_READ_CLIENT);
1188
        if (!cid->dconf.fake_async) {
1301
        if (!cid->dconf.fake_async) {
1189
            if (cid->dconf.log_unsupported)
1302
            if (cid->dconf.log_unsupported)
1190
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1303
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
Lines 1533-1538 Link Here
1533
        }
1646
        }
1534
1647
1535
        if (res < 0) {
1648
        if (res < 0) {
1649
			/* LTAC: Just return error, do not advice ISAPI module to unload. Because if ISAPI module is unloaded from
1650
			 * memory, in scenarios where there are outstanding Isapi Requests, mod_isapi would result in access 
1651
			 * violation/crash! at Isapi module entry point, i.e., at (*isa->HttpExtensionProc)(cid->ecb) call. The 
1652
			 * ideal fix would be invoke unload call only if there are no outstanding Isapi requests pending, which is 
1653
			 * hard to deduce in existing pipelined architecture.			
1654
			 *isapi_unload(isa, 0);
1655
             */
1536
            return HTTP_INTERNAL_SERVER_ERROR;
1656
            return HTTP_INTERNAL_SERVER_ERROR;
1537
        }
1657
        }
1538
1658
Lines 1551-1557 Link Here
1551
        cid->ecb->lpbData = NULL;
1671
        cid->ecb->lpbData = NULL;
1552
    }
1672
    }
1553
1673
1554
    /* To emulate async behavior...
1674
    
1675
    /* To emulate async behavior **only if AcceptEx is disabled**...
1555
     *
1676
     *
1556
     * We create a cid->completed mutex and lock on it so that the
1677
     * We create a cid->completed mutex and lock on it so that the
1557
     * app can believe is it running async.
1678
     * app can believe is it running async.
Lines 1563-1569 Link Here
1563
     * which may *only* happen once HSE_REQ_DONE_WITH_SESSION has
1684
     * which may *only* happen once HSE_REQ_DONE_WITH_SESSION has
1564
     * unlocked the mutex.
1685
     * unlocked the mutex.
1565
     */
1686
     */
1566
    if (cid->dconf.fake_async) {
1687
    if (cid->dconf.fake_async && 
1688
        r->connection->hAsyncDispatchIOCP == NULL){       /* LTAC: IOCP not used? */
1567
        rv = apr_thread_mutex_create(&cid->completed,
1689
        rv = apr_thread_mutex_create(&cid->completed,
1568
                                     APR_THREAD_MUTEX_UNNESTED,
1690
                                     APR_THREAD_MUTEX_UNNESTED,
1569
                                     r->pool);
1691
                                     r->pool);
Lines 1578-1583 Link Here
1578
        }
1700
        }
1579
    }
1701
    }
1580
1702
1703
    r->connection->nUsageCount++;
1704
    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1705
                   "Thread(%04d) Connection(%05d, %s, %d) ISAPI: %s: %s", 
1706
                   GetCurrentThreadId(), r->connection->id, "", r->connection
1707
                   ->nUsageCount, r->uri, r->args);
1581
    /* All right... try and run the sucker */
1708
    /* All right... try and run the sucker */
1582
    rv = (*isa->HttpExtensionProc)(cid->ecb);
1709
    rv = (*isa->HttpExtensionProc)(cid->ecb);
1583
1710
Lines 1601-1606 Link Here
1601
            break;
1728
            break;
1602
1729
1603
        case HSE_STATUS_PENDING:
1730
        case HSE_STATUS_PENDING:
1731
            if (r->connection != NULL &&
1732
                r->connection->hAsyncDispatchIOCP != NULL){
1733
1734
                int nLastAsyncRequest = GetNotes(r);             
1735
1736
                /* Check whether Async request was already emulated and 
1737
                 * completed by Apache (Transmit file or Async read 
1738
                 * operation) on this request?
1739
                 */
1740
                if (nLastAsyncRequest != HSE_REQ_DONE_WITH_SESSION){                                                        
1741
                        SetNotes(r, HSE_STATUS_PENDING);
1742
                        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1743
                                "Thread(%04d) Connection(%05d,%s) ISAPI: %s: %s ",
1744
                                GetCurrentThreadId(), 0, "ret HSE_STATUS_PENDING", r->uri, r->args);
1745
                        
1746
                        return PENDING;
1747
                }
1748
            }            
1749
1604
            /* emulating async behavior...
1750
            /* emulating async behavior...
1605
             */
1751
             */
1606
            if (cid->completed) {
1752
            if (cid->completed) {
Lines 1659-1664 Link Here
1659
                          "complete the response: %s ", r->filename);
1805
                          "complete the response: %s ", r->filename);
1660
        }
1806
        }
1661
1807
1808
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1809
                   "Thread(%04d) Connection(%05d,%s) ISAPI: %s: %s", 
1810
                   GetCurrentThreadId(), r->connection->id, "ret OK", r->uri, r->args);
1811
1662
        return OK; /* NOT r->status, even if it has changed. */
1812
        return OK; /* NOT r->status, even if it has changed. */
1663
    }
1813
    }
1664
1814
Lines 1669-1674 Link Here
1669
        r->status = cid->ecb->dwHttpStatusCode;
1819
        r->status = cid->ecb->dwHttpStatusCode;
1670
    }
1820
    }
1671
1821
1822
    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1823
                   "Thread(%04d) Connection(%05d,%s%d) ISAPI: %s: %s", 
1824
                   GetCurrentThreadId(), r->connection->id, "ret ", r->status, r->uri, r->args);
1825
1672
    /* For all missing-response situations simply return the status,
1826
    /* For all missing-response situations simply return the status,
1673
     * and let the core respond to the client.
1827
     * and let the core respond to the client.
1674
     */
1828
     */
(-)httpd-2.2.17_orig/modules/arch/win32/mod_isapi.h (-4 / +6 lines)
Lines 242-249 Link Here
242
242
243
/* Valid flags passed with TerminateExtension()
243
/* Valid flags passed with TerminateExtension()
244
 */
244
 */
245
#define HSE_TERM_MUST_UNLOAD      1
245
/* LTAC: Comply with MS SDK values*/
246
#define HSE_TERM_ADVISORY_UNLOAD  2
246
#define HSE_TERM_MUST_UNLOAD      2
247
#define HSE_TERM_ADVISORY_UNLOAD  1
247
248
248
/* The shutdown entry point óptionally exported by an ISAPI handler, passed
249
/* The shutdown entry point óptionally exported by an ISAPI handler, passed
249
 * HSE_TERM_MUST_UNLOAD or HSE_TERM_ADVISORY_UNLOAD.  The module may return 
250
 * HSE_TERM_MUST_UNLOAD or HSE_TERM_ADVISORY_UNLOAD.  The module may return 
Lines 259-266 Link Here
259
 * will remain loaded, or 1 if it consents to being unloaded. If the module
260
 * will remain loaded, or 1 if it consents to being unloaded. If the module
260
 * is passed HSE_TERM_MUST_UNLOAD, it's return value is ignored.
261
 * is passed HSE_TERM_MUST_UNLOAD, it's return value is ignored.
261
 */
262
 */
262
#define HSE_TERM_MUST_UNLOAD      1
263
/* LTAC: Comply with MS SDK values. (Redefined to address non-VC compiler issue?)*/
263
#define HSE_TERM_ADVISORY_UNLOAD  2
264
#define HSE_TERM_MUST_UNLOAD      2
265
#define HSE_TERM_ADVISORY_UNLOAD  1
264
266
265
#ifdef __cplusplus
267
#ifdef __cplusplus
266
}
268
}
(-)httpd-2.2.17_orig/modules/http/http_core.c (-2 / +56 lines)
Lines 34-39 Link Here
34
#include "scoreboard.h"
34
#include "scoreboard.h"
35
35
36
#include "mod_core.h"
36
#include "mod_core.h"
37
#include "apr_atomic.h"     // For Async framework
38
#include "http_log.h"
37
39
38
/* Handles for core filters */
40
/* Handles for core filters */
39
AP_DECLARE_DATA ap_filter_rec_t *ap_http_input_filter_handle;
41
AP_DECLARE_DATA ap_filter_rec_t *ap_http_input_filter_handle;
Lines 125-130 Link Here
125
{
127
{
126
    request_rec *r;
128
    request_rec *r;
127
    conn_state_t *cs = c->cs;
129
    conn_state_t *cs = c->cs;
130
    int rv = 0;
128
131
129
    if (c->clogging_input_filters) {
132
    if (c->clogging_input_filters) {
130
        return ap_process_http_connection(c);
133
        return ap_process_http_connection(c);
Lines 142-152 Link Here
142
145
143
            ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r);
146
            ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r);
144
            if (r->status == HTTP_OK)
147
            if (r->status == HTTP_OK)
145
                ap_process_request(r);
148
                rv = ap_process_request(r);
146
149
147
            if (ap_extended_status)
150
            if (ap_extended_status)
148
                ap_increment_counts(c->sbh, r);
151
                ap_increment_counts(c->sbh, r);
149
152
153
154
            /* LTAC: In case of PENDING operation return immediately.
155
             * Each PENDING operation complete can be signalled to 
156
             * client only by closing the connection, hence further
157
             * processing the other requests on same connection can 
158
             * again result in yet another PENDING operation 
159
             * notifying such operations can only be done by closing the
160
             * socket.
161
             */
162
            if (rv == PENDING){
163
                apr_atomic_set32((volatile apr_uint32_t*) &c->opState, 
164
                                 CONN_OPSTATE_ASYNC_PRE_READY);
165
                break;
166
            }
167
150
            if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted
168
            if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted
151
                    || ap_graceful_stop_signalled()) {
169
                    || ap_graceful_stop_signalled()) {
152
                cs->state = CONN_STATE_LINGER;
170
                cs->state = CONN_STATE_LINGER;
Lines 173-197 Link Here
173
{
191
{
174
    request_rec *r;
192
    request_rec *r;
175
    apr_socket_t *csd = NULL;
193
    apr_socket_t *csd = NULL;
194
    int rv = 0;
176
195
177
    /*
196
    /*
178
     * Read and process each request found on our connection
197
     * Read and process each request found on our connection
179
     * until no requests are left or we decide to close.
198
     * until no requests are left or we decide to close.
180
     */
199
     */
181
200
201
    int nTotalReqInConnection = 0;
182
    ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
202
    ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
183
    while ((r = ap_read_request(c)) != NULL) {
203
    while ((r = ap_read_request(c)) != NULL) {
184
204
205
        AP_LOG_CERROR(APLOG_MARK, APLOG_DEBUG, 0, c,
206
            "Thread(%04d) --%d-- Connection(%05d,%s) context: %08x", 
207
            GetCurrentThreadId(), ++nTotalReqInConnection, c->id, r->uri,
208
            c->pContext);
209
185
        c->keepalive = AP_CONN_UNKNOWN;
210
        c->keepalive = AP_CONN_UNKNOWN;
186
        /* process the request if it was read without error */
211
        /* process the request if it was read without error */
187
212
188
        ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r);
213
        ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r);
189
        if (r->status == HTTP_OK)
214
        if (r->status == HTTP_OK)
190
            ap_process_request(r);
215
            rv = ap_process_request(r);
191
216
192
        if (ap_extended_status)
217
        if (ap_extended_status)
193
            ap_increment_counts(c->sbh, r);
218
            ap_increment_counts(c->sbh, r);
194
219
220
        /* LTAC: In case of PENDING operation return immediately.
221
         * Each PENDING operation completion can be signalled to 
222
         * client only by closing the connection, hence further
223
         * processing the other requests on same connection can 
224
         * again result in yet another PENDING operation which
225
         * is not supported atleast for now.
226
         */
227
        if (rv == PENDING){
228
            /* LTAC: Update score board is not required since a connection is 
229
             * no longer tied to a thread, so we don't need to update the 
230
             * score board now
231
             */
232
             /* ap_update_child_status(c->sbh, SERVER_BUSY_KEEPALIVE, r); */
233
             
234
            /* LTAC: Update connection operational state 
235
             */
236
            apr_atomic_set32((volatile apr_uint32_t*) &r->connection->opState,
237
                             CONN_OPSTATE_ASYNC_PRE_READY);
238
            break;
239
        }
240
195
        if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted)
241
        if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted)
196
            break;
242
            break;
197
243
Lines 223-228 Link Here
223
                                    NULL, r, r->connection);
269
                                    NULL, r, r->connection);
224
        ap_add_output_filter_handle(ap_http_outerror_filter_handle,
270
        ap_add_output_filter_handle(ap_http_outerror_filter_handle,
225
                                    NULL, r, r->connection);
271
                                    NULL, r, r->connection);
272
273
#ifdef _WIN32
274
        /* LTAC: Hook async filter only if IOCP is present */
275
        if (r->connection->hAsyncDispatchIOCP){
276
            ap_add_output_filter_handle(ap_core_async_output_filter_handle,
277
                                        NULL, r, r->connection);
278
        }
279
#endif /*_WIN32*/
226
    }
280
    }
227
281
228
    return OK;
282
    return OK;
(-)httpd-2.2.17_orig/modules/http/http_request.c (-5 / +25 lines)
Lines 215-222 Link Here
215
    ap_send_error_response(r_1st_err, recursive_error);
215
    ap_send_error_response(r_1st_err, recursive_error);
216
}
216
}
217
217
218
static void check_pipeline_flush(request_rec *r)
218
static int check_pipeline_flush(request_rec *r)
219
{
219
{
220
    int status = APR_SUCCESS;
220
    apr_bucket *e;
221
    apr_bucket *e;
221
    apr_bucket_brigade *bb;
222
    apr_bucket_brigade *bb;
222
    conn_rec *c = r->connection;
223
    conn_rec *c = r->connection;
Lines 239-245 Link Here
239
        }
240
        }
240
        else {
241
        else {
241
            c->data_in_input_filters = 1;
242
            c->data_in_input_filters = 1;
242
            return;    /* don't flush */
243
            return status;    /* don't flush */
243
        }
244
        }
244
    }
245
    }
245
246
Lines 252-261 Link Here
252
         * if something hasn't been sent to the network yet.
253
         * if something hasn't been sent to the network yet.
253
         */
254
         */
254
        APR_BRIGADE_INSERT_HEAD(bb, e);
255
        APR_BRIGADE_INSERT_HEAD(bb, e);
255
        ap_pass_brigade(r->connection->output_filters, bb);
256
        return ap_pass_brigade(r->connection->output_filters, bb);
256
}
257
}
257
258
258
void ap_process_request(request_rec *r)
259
int ap_process_request(request_rec *r)
259
{
260
{
260
    int access_status;
261
    int access_status;
261
262
Lines 283-288 Link Here
283
        }
284
        }
284
    }
285
    }
285
286
287
    if (access_status == PENDING)
288
    {        
289
        /* LTAC: For Async framework, this state represents a long time consuming 
290
         * operation 
291
         */
292
        r->connection->pRequest = r;
293
        return PENDING;
294
    }
295
286
    if (access_status == DONE) {
296
    if (access_status == DONE) {
287
        /* e.g., something not in storage like TRACE */
297
        /* e.g., something not in storage like TRACE */
288
        access_status = OK;
298
        access_status = OK;
Lines 303-313 Link Here
303
     * this packet, then it'll appear like the link is stalled when really
313
     * this packet, then it'll appear like the link is stalled when really
304
     * it's the application that's stalled.
314
     * it's the application that's stalled.
305
     */
315
     */
306
    check_pipeline_flush(r);
316
    access_status = check_pipeline_flush(r);
317
318
    /* Async send file initiated ? */
319
    if (access_status == PENDING){
320
        ap_assert(r->connection->hAsyncDispatchIOCP);    
321
        ap_assert(r->connection->AsyncOperation.bIsLinkedToIOCP); 
322
        r->connection->pRequest = r;
323
        return PENDING;
324
    }
325
307
    ap_update_child_status(r->connection->sbh, SERVER_BUSY_LOG, r);
326
    ap_update_child_status(r->connection->sbh, SERVER_BUSY_LOG, r);
308
    ap_run_log_transaction(r);
327
    ap_run_log_transaction(r);
309
    if (ap_extended_status)
328
    if (ap_extended_status)
310
        ap_time_process_request(r->connection->sbh, STOP_PREQUEST);
329
        ap_time_process_request(r->connection->sbh, STOP_PREQUEST);
330
	return access_status;
311
}
331
}
312
332
313
static apr_table_t *rename_original_env(apr_pool_t *p, apr_table_t *t)
333
static apr_table_t *rename_original_env(apr_pool_t *p, apr_table_t *t)
(-)httpd-2.2.17_orig/server/config.c (-1 / +5 lines)
Lines 381-388 Link Here
381
        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
381
        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
382
            "handler \"%s\" not found for: %s", r->handler, r->filename);
382
            "handler \"%s\" not found for: %s", r->handler, r->filename);
383
    }
383
    }
384
    /* LTAC: For Async framework - make PENDING to be an acceptable return
385
     * value as well, PENDING - represents either Async I/O was initiated
386
     * or a Long duration operation (push connection) was invoked
387
     */
384
    if ((result != OK) && (result != DONE) && (result != DECLINED)
388
    if ((result != OK) && (result != DONE) && (result != DECLINED)
385
        && (result != AP_FILTER_ERROR)
389
        && (result != PENDING) && (result != AP_FILTER_ERROR)
386
        && !ap_is_HTTP_VALID_RESPONSE(result)) {
390
        && !ap_is_HTTP_VALID_RESPONSE(result)) {
387
        /* If a module is deliberately returning something else
391
        /* If a module is deliberately returning something else
388
         * (request_rec in non-HTTP or proprietary extension?)
392
         * (request_rec in non-HTTP or proprietary extension?)
(-)httpd-2.2.17_orig/server/connection.c (+5 lines)
Lines 108-113 Link Here
108
108
109
    ap_update_child_status(c->sbh, SERVER_CLOSING, NULL);
109
    ap_update_child_status(c->sbh, SERVER_CLOSING, NULL);
110
110
111
    AP_LOG_CERROR(APLOG_MARK, APLOG_DEBUG, 0, c,
112
                   "Thread(%04d) Connection(%05d,%s, %d) Socket: %d",
113
                   GetCurrentThreadId(), c->id, "CLOSING", c->nUsageCount, 
114
                   *(((BYTE*)csd) + sizeof(apr_pool_t*))); /*get socket handle*/
115
111
#ifdef NO_LINGCLOSE
116
#ifdef NO_LINGCLOSE
112
    ap_flush_conn(c); /* just close it */
117
    ap_flush_conn(c); /* just close it */
113
    apr_socket_close(csd);
118
    apr_socket_close(csd);
(-)httpd-2.2.17_orig/server/core.c (+5 lines)
Lines 3757-3762 Link Here
3757
        APR_BRIGADE_INSERT_TAIL(bb, e);
3757
        APR_BRIGADE_INSERT_TAIL(bb, e);
3758
3758
3759
        status = ap_pass_brigade(r->output_filters, bb);
3759
        status = ap_pass_brigade(r->output_filters, bb);
3760
3761
        /* Async ? */        
3762
        if (status == PENDING && c->aborted == 0){            
3763
            return PENDING;
3764
        }
3760
        if (status == APR_SUCCESS
3765
        if (status == APR_SUCCESS
3761
            || r->status != HTTP_OK
3766
            || r->status != HTTP_OK
3762
            || c->aborted) {
3767
            || c->aborted) {
(-)httpd-2.2.17_orig/server/core_filters.c (-2 / +121 lines)
Lines 56-61 Link Here
56
56
57
#include "mod_so.h" /* for ap_find_loaded_module_symbol */
57
#include "mod_so.h" /* for ap_find_loaded_module_symbol */
58
58
59
/* For Async Send file feature */
60
#ifdef _WIN32
61
#include "mpm\winnt\mpm_winnt.h"
62
#include "..\srclib\apr\include\arch\win32\apr_arch_networkio.h"
63
#endif /*_WIN32*/
64
59
#define AP_MIN_SENDFILE_BYTES           (256)
65
#define AP_MIN_SENDFILE_BYTES           (256)
60
66
61
/**
67
/**
Lines 349-354 Link Here
349
    return APR_SUCCESS;
355
    return APR_SUCCESS;
350
}
356
}
351
357
358
#ifdef _WIN32
359
360
__inline apr_status_t do_sendfile_ex(apr_socket_t* client_socket,
361
									apr_file_t* fd,
362
									apr_hdtr_t* hdtr,
363
									apr_off_t  file_offset,
364
									apr_size_t  file_bytes_left,
365
									apr_int32_t flags,
366
									conn_rec* pConnRec)
367
{
368
	apr_status_t rv = APR_BADARG;
369
	apr_size_t tmplen = file_bytes_left;
370
371
	ap_assert(pConnRec != NULL);
372
	ap_assert(pConnRec->AsyncOperation.bIsLinkedToIOCP == 0);
373
	ap_assert(pConnRec->AsyncOperation.bAsyncSendFile);
374
	
375
	if (client_socket == NULL){
376
		return rv;
377
	}
378
379
	/* Associate the context's overlapped info to tcp socket, since 
380
     * connection is accepted, it is kept unused */
381
    client_socket->overlapped = &((PCOMP_CONTEXT)pConnRec->pContext)
382
                                    ->Overlapped;
383
    /* We use IOCP and not events any more */
384
    ap_assert(client_socket->overlapped->hEvent == NULL);    
385
    
386
    rv = apr_socket_sendfileEx(client_socket, fd, hdtr, &file_offset,
387
							   &tmplen, flags, pConnRec->hAsyncDispatchIOCP);
388
389
    switch (rv)
390
    {
391
    case ERROR_IO_PENDING:                
392
        pConnRec->AsyncOperation.bIsLinkedToIOCP = 1;                
393
        AP_LOG_CERROR(APLOG_MARK, APLOG_DEBUG, 0, pConnRec, 
394
                      "Thread(%04d) Invoked TransmitFile(%08x) ",
395
                      GetCurrentThreadId(), client_socket->socketdes);                   
396
        rv = PENDING; 
397
		break;
398
        
399
    case WSAEWOULDBLOCK:
400
        AP_LOG_CERROR(APLOG_MARK, APLOG_ERR, 0, pConnRec, 
401
                     "Thread(%04d) Cannot invoke TransmitFile(%08x) "
402
                     "without overlapped structure falling back to "
403
                     "Sync sendfile", GetCurrentThreadId(), 
404
                     client_socket->socketdes);            
405
        break;
406
407
    case ERROR_UNKNOWN_PORT:                
408
        rv = apr_get_os_error();
409
        AP_LOG_CERROR(APLOG_MARK, APLOG_ERR, 0, pConnRec, 
410
                     "Thread(%04d) Cannot assoc CompletionPort for "
411
					 "TransmitFile(%08x) to socket(), falling back to "
412
					 "Sync sendfile", GetCurrentThreadId(), 
413
					 pConnRec->hAsyncDispatchIOCP, client_socket);
414
        break;
415
416
    case APR_SUCCESS:
417
		AP_LOG_CERROR(APLOG_MARK, APLOG_WARNING, 0, pConnRec, 
418
					  "Thread(%04d) Async IO completed upon request "
419
					  "TransmitFile(%08x) ", GetCurrentThreadId(), 
420
					  pConnRec->hAsyncDispatchIOCP, client_socket);
421
        break;
422
        
423
    default:
424
        AP_LOG_CERROR(APLOG_MARK, APLOG_ERR, rv, pConnRec, 
425
                      "Thread(%04d) Error while invoking "
426
                      "apr_socket_sendfileEx for socket %08x"
427
                      "falling back to sync sendfile()",
428
                      GetCurrentThreadId(),client_socket->socketdes);
429
    }
430
431
	return rv;
432
}
433
434
#endif /*_WIN32*/
435
352
/* sendfile_it_all()
436
/* sendfile_it_all()
353
 *  send the entire file using sendfile()
437
 *  send the entire file using sendfile()
354
 *  handle partial writes
438
 *  handle partial writes
Lines 363-369 Link Here
363
                                    apr_size_t  file_bytes_left,
447
                                    apr_size_t  file_bytes_left,
364
                                    apr_size_t  total_bytes_left,
448
                                    apr_size_t  total_bytes_left,
365
                                    apr_size_t  *bytes_sent,
449
                                    apr_size_t  *bytes_sent,
366
                                    apr_int32_t flags)
450
                                    apr_int32_t flags,
451
                                    conn_rec* pConnRec)
367
{
452
{
368
    apr_status_t rv;
453
    apr_status_t rv;
369
#ifdef AP_DEBUG
454
#ifdef AP_DEBUG
Lines 380-385 Link Here
380
    do {
465
    do {
381
        apr_size_t tmplen = file_bytes_left;
466
        apr_size_t tmplen = file_bytes_left;
382
467
468
#if _WIN32        
469
        /* Check whether Async file send operation is possible:
470
        *  1. Connection record should not be NULL
471
        *  2. Socket should not be already associated to IOCP - error check
472
        *  3. Connection should be set to use Async Write
473
        */
474
        if (pConnRec != NULL  &&        
475
            pConnRec->AsyncOperation.bIsLinkedToIOCP == 0 &&    
476
            pConnRec->AsyncOperation.bAsyncSendFile){
477
478
			/* Call SendFileEx */
479
			rv = do_sendfile_ex(c->client_socket, fd, hdtr, file_offset, 
480
								tmplen, flags, pConnRec);			           
481
482
			/* Async File write initiated, return immediately */
483
			if (rv == PENDING){
484
				return rv;
485
			} 
486
487
            /* Fall back to Stock Sendfile in case of TransmitFile 
488
			 * initialization errors, errors during transmission will be caught 
489
			 * by the code path handling Completion packet deqeueing method -
490
			 * mpm_winnt_get_connection() and HandlePendingContext()
491
             */
492
        }
493
        
494
#endif /* _WIN32 */
495
        
383
        rv = apr_socket_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen,
496
        rv = apr_socket_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen,
384
                                 flags);
497
                                 flags);
385
        *bytes_sent += tmplen;
498
        *bytes_sent += tmplen;
Lines 849-855 Link Here
849
                                                       headers              */
962
                                                       headers              */
850
                                     &bytes_sent,   /* how many bytes were
963
                                     &bytes_sent,   /* how many bytes were
851
                                                       sent                 */
964
                                                       sent                 */
852
                                     flags);   /* apr_sendfile flags        */
965
                                     flags,         /* apr_sendfile flags   */
966
                                     c);
967
968
                /* Async send file ? */
969
                if (rv == PENDING){   
970
                    return PENDING;
971
                }                
853
            }
972
            }
854
            else
973
            else
855
#endif
974
#endif
(-)httpd-2.2.17_orig/server/mpm/winnt/child.c (-23 / +837 lines)
Lines 67-73 Link Here
67
static int max_num_completion_contexts = 0;
67
static int max_num_completion_contexts = 0;
68
static HANDLE ThreadDispatchIOCP = NULL;
68
static HANDLE ThreadDispatchIOCP = NULL;
69
static HANDLE qwait_event = NULL;
69
static HANDLE qwait_event = NULL;
70
void HandlePendingContext(PCOMP_CONTEXT pContext, ap_sb_handle_t *pSbh);
70
71
72
/* LTAC: Store the actual handle of the IO completion port that the worker
73
 * thread is supposed to work on. Stored in Thread local storage area so 
74
 * that each thread can be associated to differnt IOCP Queue efficiently.
75
 * This way we make specific thread to work on specific completion ports 
76
 * (Connection acceptence or Async Callback IOCP port)   
77
 */
78
__declspec(thread) static HANDLE tls_hIOCP = 0;
79
80
/* If "DedicatedThreadsForAsyncCallbacks" experimental httpd configuration
81
 * item is set, AsyncDispatchIOCP will store the IOCP handle specifically 
82
 * meant for Async callback notifications.
83
 */
84
static HANDLE AsyncDispatchIOCP = NULL;
85
86
/* Holds the count of dedicated threads initialized for Async 
87
 * completion notification processing.
88
 */
89
static int s_nAsyncHandlingThreadsInit = 0;    
71
90
72
void mpm_recycle_completion_context(PCOMP_CONTEXT context)
91
void mpm_recycle_completion_context(PCOMP_CONTEXT context)
73
{
92
{
Lines 95-100 Link Here
95
    }
114
    }
96
}
115
}
97
116
117
118
119
void mpm_recycle_iocp_completion_context(PCOMP_CONTEXT context)
120
{
121
122
    /* LTAC: We use Async framework in AcceptEx mode - where one thread
123
     * is not always associated to one context/connection - since a 
124
     * context can result in a Async context/connection which has to be 
125
     * queued seperately for later usage and cleanup. 
126
     * Therefore in Async / AcceptEx mode - we cleanup the context once
127
     * we are done with the sync/async connection 
128
      
129
     * LTAC TODO OPTIMIZATION: 
130
     * We can try to optimize by truly recycling the context rather than 
131
	 * freeing and creating contexts every time - this saves us some CPU 
132
	 * cycles, however - when we recycle, memory used within the context 
133
	 * (transaction pool to be precise) doesn't get cleaned up until the 
134
	 * child restarts. This is something we may need to fix it in core 
135
	 * Apache if we really want to recycle completion contexts for performance
136
	 * reasons
137
     *
138
     * Note:
139
     * context->accept_socket may be in a disconnected but reusable
140
     * state so -don't- close it.
141
     */        
142
    ap_assert(use_acceptex);
143
    ap_assert(ThreadDispatchIOCP != NULL);
144
145
    if (context) {                    
146
            if(context->pAsyncConRec != NULL){
147
                if (context->pAsyncConRec->opState != CONN_OPSTATE_ASYNC_DONE ){
148
                    /* LTAC: This is an Async context don't clean up 
149
                     */
150
                    AP_LOG_CERROR(APLOG_MARK,APLOG_WARNING, 0, context->pAsyncConRec,
151
                        "Thread(%04d) Connection(%05d,%s) Not Destroying async context since state is pending: %08x",
152
                     GetCurrentThreadId(), context->pAsyncConRec->id, "", context);
153
                    return;
154
                }
155
                else{
156
                    AP_LOG_CERROR(APLOG_MARK,APLOG_DEBUG, 0, context->pAsyncConRec,
157
                        "Thread(%04d) Connection(%05d,%s) Destroying async context: %08x",
158
                     GetCurrentThreadId(), context->pAsyncConRec->id, "", context);
159
                }
160
            }
161
            else{
162
                AP_LOG_ERROR(APLOG_MARK,APLOG_DEBUG, 0, ap_server_conf,
163
                  "Thread(%04d) Connection(%05d,%s) Destroying context: %08x",
164
                     GetCurrentThreadId(), 0, "", context);         
165
            }
166
167
            /* Acquire child lock
168
             */
169
            apr_thread_mutex_lock(child_lock);
170
171
            /* By now this event should not exist but do a sanity cleanup
172
             * TODO: remove this later
173
             */
174
            if (context->Overlapped.hEvent != NULL){
175
                CloseHandle(context->Overlapped.hEvent);
176
                context->Overlapped.hEvent = NULL;
177
            }
178
               
179
            apr_pool_destroy(context->ptrans);
180
            apr_atomic_dec32(&num_completion_contexts);
181
            free(context);                
182
183
            /* Release lock */
184
            apr_thread_mutex_unlock(child_lock);
185
186
            context = NULL;   
187
    }
188
}
189
190
98
PCOMP_CONTEXT mpm_get_completion_context(void)
191
PCOMP_CONTEXT mpm_get_completion_context(void)
99
{
192
{
100
    apr_status_t rv;
193
    apr_status_t rv;
Lines 178-184 Link Here
178
                context->accept_socket = INVALID_SOCKET;
271
                context->accept_socket = INVALID_SOCKET;
179
                context->ba = apr_bucket_alloc_create(context->ptrans);
272
                context->ba = apr_bucket_alloc_create(context->ptrans);
180
                apr_atomic_inc32(&num_completion_contexts);
273
                apr_atomic_inc32(&num_completion_contexts);
181
182
                apr_thread_mutex_unlock(child_lock);
274
                apr_thread_mutex_unlock(child_lock);
183
                break;
275
                break;
184
            }
276
            }
Lines 191-196 Link Here
191
    return context;
283
    return context;
192
}
284
}
193
285
286
287
288
PCOMP_CONTEXT mpm_get_Iocp_completion_context(void)
289
{
290
    apr_status_t rv;
291
    PCOMP_CONTEXT context = NULL;
292
    
293
    int max_num_contexts = g_MaxConnectionsToQueuePerChild;
294
295
    ap_assert(use_acceptex);
296
    ap_assert(ThreadDispatchIOCP != NULL);
297
298
    /* LTAC: Throttle connection accept based on number of completion/
299
     * connection contexts    
300
     * We account only for Sync completion context - since these are the 
301
     * connections that are actually eating httpd.exe - CPU time slice,
302
     * hence we deduct the Async contexts
303
     */    
304
    if (num_completion_contexts - g_AsyncContextCount >= max_num_contexts ){
305
        static DWORD dwLastTime = 0;
306
307
        /* Log about this resource contention very first time or 
308
         * every 10 minutes 
309
         */
310
        if (dwLastTime == 0 ||
311
            GetTickCount() - dwLastTime > 10*60*1000 /*10 minutes*/){      
312
                AP_LOG_ERROR(APLOG_MARK,APLOG_WARNING, 0, ap_server_conf,
313
                    "Thread(%04d) Contexts(Total: %d Sync: %d Async: %d "
314
                    "Max: %d) returning null context", GetCurrentThreadId(), 
315
                    num_completion_contexts,  num_completion_contexts - 
316
                    g_AsyncContextCount, g_AsyncContextCount, max_num_contexts);
317
318
                dwLastTime = GetTickCount();
319
        }
320
321
        /* Wait for a second for accept thread to free CPU
322
         * TODO: Make the sleep interval configurable if accept thread is 
323
         * eating more CPU?
324
         */
325
        Sleep(1000);
326
        return NULL;
327
    }
328
329
    while (1) {
330
        apr_allocator_t *allocator;       
331
332
        apr_thread_mutex_lock(child_lock);
333
334
        /* LTAC: Dont use APR to allocate contexts, doing so will spike memory
335
         * usage, this is because garbage collection of free'd APR memory 
336
         * happens only when child httpd process die's
337
         */
338
        context = (PCOMP_CONTEXT) malloc(sizeof(COMP_CONTEXT));
339
        memset(context, 0, sizeof(COMP_CONTEXT));
340
341
        /* This event is not really required if we are using Apache single 
342
         * process mode - refer win_acceptIocp() function for more info
343
         */
344
        context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
345
        if (context->Overlapped.hEvent == NULL) {
346
            /* Hopefully this is a temporary condition ... */
347
            ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_os_error(), ap_server_conf,
348
                "mpm_get_Iocp_completion_context: CreateEvent failed.");
349
            free(context);
350
            apr_thread_mutex_unlock(child_lock);
351
            return NULL;
352
        }
353
354
        /* Create the tranaction pool */
355
        apr_allocator_create(&allocator);
356
        apr_allocator_max_free_set(allocator, ap_max_mem_free);
357
        rv = apr_pool_create_ex(&context->ptrans, pchild, NULL, allocator);
358
        if (rv != APR_SUCCESS) {
359
            ap_log_error(APLOG_MARK,APLOG_WARNING, rv, ap_server_conf,
360
                "mpm_get_Iocp_completion_context: Failed to create the transaction pool.");
361
            CloseHandle(context->Overlapped.hEvent);
362
            free(context);            
363
            apr_thread_mutex_unlock(child_lock);
364
            return NULL;
365
        }
366
        apr_allocator_owner_set(allocator, context->ptrans);
367
        apr_pool_tag(context->ptrans, "IocpTransaction");
368
369
        context->accept_socket = INVALID_SOCKET;
370
        context->ba = apr_bucket_alloc_create(context->ptrans);
371
372
        /* LTAC: Setup new Async entities within context
373
         */
374
        context->pAsyncConRec = NULL;
375
        context->next = NULL;                
376
        apr_atomic_inc32(&num_completion_contexts);
377
378
        apr_thread_mutex_unlock(child_lock);
379
        break;    
380
    }
381
382
    return context;
383
}
384
194
apr_status_t mpm_post_completion_context(PCOMP_CONTEXT context,
385
apr_status_t mpm_post_completion_context(PCOMP_CONTEXT context,
195
                                         io_state_e state)
386
                                         io_state_e state)
196
{
387
{
Lines 681-698 Link Here
681
}
872
}
682
873
683
874
875
/* Windows NT/2000 specific code...
876
 * This function is modified version of of winnt_accept(). 
877
 * Accept processing for on Windows NT uses a producer/consumer queue
878
 * An accept thread accepts connections off the network using IO compeletion
879
 * ports then issues PostQueuedCompletionStatus() to awake a thread blocked 
880
 * on the ThreadDispatch IOCompletionPort (IOCP)
881
 *
882
 * winnt_acceptIocp()
883
 *    One or more accept threads run in this function, each of which accepts
884
 *    connections off the network using Completion ports and calls 
885
 *    PostQueuedCompletionStatus() to queue an io completion packet to the 
886
 *    ThreadDispatch IOCompletionPort.
887
 * winnt_get_connection()
888
 *    Worker threads block on the ThreadDispatch IOCompletionPort awaiting
889
 *    connections to service.
890
 */
891
static unsigned int __stdcall winnt_acceptIocp(void *lr_)
892
{
893
    ap_listen_rec *lr = (ap_listen_rec *)lr_;
894
    apr_os_sock_info_t sockinfo;
895
    PCOMP_CONTEXT context = NULL;
896
    DWORD BytesRead = 0;
897
    SOCKET nlsd;
898
    int rv, err_count = 0;    
899
900
    DWORD dwCompKey = IOCP_CONNECTION_ACCEPTED;
901
    LPOVERLAPPED pOvl = 0;    
902
    HANDLE hTemp = 0;
903
    int nRetry = 0;
904
905
#if APR_HAVE_IPV6
906
    SOCKADDR_STORAGE ss_listen;
907
    int namelen = sizeof(ss_listen);
908
#endif
909
    
910
   
911
    apr_os_sock_get(&nlsd, lr->sd);
912
        
913
914
#if APR_HAVE_IPV6
915
    if (getsockname(nlsd, (struct sockaddr *)&ss_listen, &namelen) == SOCKET_ERROR) {
916
        ap_log_error(APLOG_MARK,APLOG_ERR, apr_get_netos_error(), ap_server_conf,
917
                    "winnt_accept: getsockname error on listening socket, is IPv6 available?");
918
        return 1;
919
   }
920
#endif
921
922
    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
923
                 "Child %d Thread %d: Starting thread to listen on port %d.",
924
                 my_pid, GetCurrentThreadId(),lr->bind_addr->port);
925
    while (!shutdown_in_progress) {
926
        /* LTAC: Clean up the old contexts before using a new one
927
         * For Async framework - the number of contexts are limited to the new
928
         * config item - "MaxConnections", and we do not queue all contexts
929
         * (a.k.a connections) for reuse as in Stock apache since each 
930
         * connection is holding socket + file resources (handles) and this 
931
         * would result in instability of apache during long runs
932
         */
933
        if (context){
934
            mpm_recycle_iocp_completion_context(context);
935
            context = NULL;            
936
        }
937
        
938
        context = mpm_get_Iocp_completion_context();
939
        if (!context) {
940
            /* Temporary resource constraint? */
941
            /* LTAC: Max connections could have been hit?
942
             */
943
            Sleep(0);
944
            continue;
945
        }
946
        
947
948
        /* Create and initialize the accept socket */
949
#if APR_HAVE_IPV6
950
        if (context->accept_socket == INVALID_SOCKET) {
951
            context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM, IPPROTO_TCP);
952
            context->socket_family = ss_listen.ss_family;
953
        }
954
        else if (context->socket_family != ss_listen.ss_family) {
955
            closesocket(context->accept_socket);
956
            context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM, IPPROTO_TCP);
957
            context->socket_family = ss_listen.ss_family;
958
        }
959
960
        if (context->accept_socket == INVALID_SOCKET) {
961
            ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
962
                         "winnt_accept: Failed to allocate an accept socket. "
963
                         "Temporary resource constraint? Try again.");
964
            Sleep(100);
965
            continue;
966
        }
967
#else
968
        if (context->accept_socket == INVALID_SOCKET) {
969
            context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
970
            if (context->accept_socket == INVALID_SOCKET) {
971
                /* Another temporary condition? */
972
                ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
973
                             "winnt_acceptIocp: Failed to allocate an accept socket. "
974
                             "Temporary resource constraint? Try again.");
975
                Sleep(100);
976
                continue;
977
            }
978
        }
979
#endif
980
        /* AcceptEx on the completion context. The completion context will be
981
         * signaled when a connection is accepted.
982
         */                                
983
        if (!AcceptEx(nlsd, context->accept_socket,
984
                      context->buff,
985
                      0,
986
                      PADDED_ADDR_SIZE,
987
                      PADDED_ADDR_SIZE,
988
                      &BytesRead,
989
                      &context->Overlapped)) {
990
            rv = apr_get_netos_error();
991
            if ((rv == APR_FROM_OS_ERROR(WSAEINVAL)) ||
992
                (rv == APR_FROM_OS_ERROR(WSAENOTSOCK))) {
993
                /* We can get here when:
994
                 * 1) the client disconnects early
995
                 * 2) TransmitFile does not properly recycle the accept socket (typically
996
                 *    because the client disconnected)
997
                 * 3) there is VPN or Firewall software installed with buggy AcceptEx implementation
998
                 * 4) the webserver is using a dynamic address that has changed
999
                 */
1000
                ++err_count;
1001
                closesocket(context->accept_socket);
1002
                context->accept_socket = INVALID_SOCKET;
1003
                if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
1004
                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
1005
                                 "Child %d: Encountered too many errors accepting client connections. "
1006
                                 "Possible causes: dynamic address renewal, or incompatible VPN or firewall software. "
1007
                                 "Try using the Win32DisableAcceptEx directive.", my_pid);
1008
                    err_count = 0;
1009
                }
1010
                continue;
1011
            }
1012
            else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) &&
1013
                     (rv != APR_FROM_OS_ERROR(WSA_IO_PENDING))) {
1014
                ++err_count;
1015
                if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
1016
                    ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
1017
                                 "Child %d: Encountered too many errors accepting client connections. "
1018
                                 "Possible causes: Unknown. "
1019
                                 "Try using the Win32DisableAcceptEx directive.", my_pid);
1020
                    err_count = 0;
1021
                }
1022
                closesocket(context->accept_socket);
1023
                context->accept_socket = INVALID_SOCKET;
1024
                continue;
1025
            }
1026
            err_count = 0;
1027
1028
            /* Wait for pending i/o.
1029
             * Wake up once per second to check for shutdown .
1030
             * XXX: We should be waiting on exit_event instead of polling
1031
             */
1032
            while (1) {
1033
                    rv = WaitForSingleObject(context->Overlapped.hEvent, 1000);                
1034
                    if (rv == WAIT_OBJECT_0) {                    
1035
                        if (context->accept_socket == INVALID_SOCKET) {
1036
                            /* socket already closed */
1037
                            break;
1038
                        }
1039
                        if (!GetOverlappedResult((HANDLE)context->accept_socket,
1040
                                                 &context->Overlapped,
1041
                                                 &BytesRead, FALSE)) {
1042
                            ap_log_error(APLOG_MARK, APLOG_WARNING,
1043
                                         apr_get_os_error(), ap_server_conf,
1044
                                 "winnt_acceptIocp: Asynchronous AcceptEx failed.");
1045
                            closesocket(context->accept_socket);
1046
                            context->accept_socket = INVALID_SOCKET;
1047
                        }
1048
                        break;
1049
                    }
1050
                
1051
                
1052
                /* WAIT_TIMEOUT */
1053
                if (shutdown_in_progress) {
1054
                    closesocket(context->accept_socket);
1055
                    context->accept_socket = INVALID_SOCKET;
1056
                    break;
1057
                }                
1058
            }
1059
            if (context->accept_socket == INVALID_SOCKET) {
1060
                continue;
1061
            }
1062
        }        
1063
1064
        err_count = 0;
1065
        /* Inherit the listen socket settings. Required for
1066
         * shutdown() to work
1067
         */
1068
        /* LTAC: LEAK! LEAK! LEAK!
1069
         * Setsockopt - results in handle leaks, internally WinSock module
1070
         * sets up a timerhandle when the following call is made to inherit 
1071
         * lisenting socket properties (running in parent httpd.exe process
1072
         * or service context)to associate this socket with the service 
1073
         * shutdown event. I don't see this essential since we close the 
1074
         * the open sockets during shutdown gracefully in at end of 
1075
         * woker_main() 
1076
         * 
1077
         *if (setsockopt(context->accept_socket, SOL_SOCKET,
1078
         *               SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd,
1079
         *               sizeof(nlsd))) {
1080
         *    ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
1081
         *                 "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
1082
         *     * Not a failure condition. Keep running. *
1083
         *}
1084
         */
1085
        /* Get the local & remote address */
1086
        GetAcceptExSockaddrs(context->buff,
1087
                             0,
1088
                             PADDED_ADDR_SIZE,
1089
                             PADDED_ADDR_SIZE,
1090
                             &context->sa_server,
1091
                             &context->sa_server_len,
1092
                             &context->sa_client,
1093
                             &context->sa_client_len);
1094
1095
        sockinfo.os_sock = &context->accept_socket;
1096
        sockinfo.local   = context->sa_server;
1097
        sockinfo.remote  = context->sa_client;
1098
        sockinfo.family  = context->sa_server->sa_family;
1099
        sockinfo.type    = SOCK_STREAM;
1100
        apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
1101
1102
        /* LTAC: The event handle is no longer required since we will utilize
1103
         * IOCP to fire events - this way we reduce the number of handles used 
1104
         * by Apache
1105
         */
1106
        CloseHandle(context->Overlapped.hEvent);
1107
        context->Overlapped.hEvent=NULL;
1108
1109
        /* When a connection is received, send an io completion notification to
1110
         * the ThreadDispatchIOCP. This function could be replaced by
1111
         * mpm_post_completion_context(), but why do an extra function call...
1112
         */
1113
        PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_CONNECTION_ACCEPTED,
1114
                                   &context->Overlapped);
1115
        context = NULL;
1116
    }
1117
    if (!shutdown_in_progress) {
1118
        /* Yow, hit an irrecoverable error! Tell the child to die. */
1119
        SetEvent(exit_event);
1120
    }
1121
    
1122
    AP_LOG_ERROR(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
1123
                 "Child %d: Thread: %d Accept thread exiting.", my_pid, 
1124
                 GetCurrentThreadId());
1125
    return 0;
1126
}
684
static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
1127
static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
685
{
1128
{
686
    int rc;
1129
    int rc;
687
    DWORD BytesRead;
1130
    DWORD BytesRead = 0;
688
    LPOVERLAPPED pol;
1131
    LPOVERLAPPED pol = NULL;
689
#ifdef _WIN64
1132
#ifdef _WIN64
690
    ULONG_PTR CompKey;
1133
    ULONG_PTR CompKey;
691
#else
1134
#else
692
    DWORD CompKey;
1135
    DWORD CompKey;
693
#endif
1136
#endif
694
1137
695
    mpm_recycle_completion_context(context);
1138
    mpm_recycle_iocp_completion_context(context);    
696
1139
697
    apr_atomic_inc32(&g_blocked_threads);
1140
    apr_atomic_inc32(&g_blocked_threads);
698
    while (1) {
1141
    while (1) {
Lines 700-712 Link Here
700
            apr_atomic_dec32(&g_blocked_threads);
1143
            apr_atomic_dec32(&g_blocked_threads);
701
            return NULL;
1144
            return NULL;
702
        }
1145
        }
703
        rc = GetQueuedCompletionStatus(ThreadDispatchIOCP, &BytesRead, &CompKey,
1146
704
                                       &pol, INFINITE);
1147
        rc = GetQueuedCompletionStatus(tls_hIOCP, &BytesRead, &CompKey, &pol, 
1148
                                       INFINITE);       
705
        if (!rc) {
1149
        if (!rc) {
706
            rc = apr_get_os_error();
1150
            rc = apr_get_os_error();
707
            ap_log_error(APLOG_MARK,APLOG_DEBUG, rc, ap_server_conf,
1151
            AP_LOG_ERROR(APLOG_MARK,APLOG_INFO, rc, ap_server_conf,
708
                             "Child %d: GetQueuedComplationStatus returned %d", my_pid, rc);
1152
                         "Thread(%04d) GetQueuedComplationStatus returned %d", 
709
            continue;
1153
                         GetCurrentThreadId(), rc);
1154
            /* Continue processing the error condition further below, there can
1155
             * be Transmit File errors. E.g. 720064 -> The specified network name
1156
             * is no longer available 
1157
             */
710
        }
1158
        }
711
1159
712
        switch (CompKey) {
1160
        switch (CompKey) {
Lines 716-722 Link Here
716
        case IOCP_SHUTDOWN:
1164
        case IOCP_SHUTDOWN:
717
            apr_atomic_dec32(&g_blocked_threads);
1165
            apr_atomic_dec32(&g_blocked_threads);
718
            return NULL;
1166
            return NULL;
1167
1168
        case IOCP_CONNECTION_PENDING_DONE:
1169
        {
1170
            // LTAC: Move Async processing to a dedicated thread if child 
1171
            // threads are too busy ?
1172
            // Once we get real data from PQA tests we can make this call
1173
            //
1174
            context = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped);                           
1175
            break;
1176
        }
1177
1178
1179
        case IOCP_TRANSMITFILE_DONE:
1180
        {         
1181
            /* LTAC: TF I/O operation is done or have error'd  */
1182
            context = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped); 
1183
1184
            if (!context && !context->pAsyncConRec){
1185
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
1186
                             "TransmitFile operation done - context/connection"
1187
                             "is invalid");
1188
                /* Cleanup the memory associated to context */
1189
                mpm_recycle_iocp_completion_context(context);
1190
                context = NULL;    
1191
            }
1192
            break;
1193
        }
1194
719
        default:
1195
        default:
1196
            AP_LOG_ERROR(APLOG_MARK,APLOG_ERR, 0, ap_server_conf,
1197
                         "Thread(%04d) Unknown completion key: %d", 
1198
                         GetCurrentThreadId(), CompKey);  
720
            apr_atomic_dec32(&g_blocked_threads);
1199
            apr_atomic_dec32(&g_blocked_threads);
721
            return NULL;
1200
            return NULL;
722
        }
1201
        }
Lines 740-745 Link Here
740
    int thread_num = (int)thread_num_val;
1219
    int thread_num = (int)thread_num_val;
741
    ap_sb_handle_t *sbh;
1220
    ap_sb_handle_t *sbh;
742
1221
1222
    /* LTAC: Initialize the TLS IOCP handle varibale during thread startup */
1223
    if (use_acceptex && tls_hIOCP == 0)
1224
    {
1225
        /* using child lock for thread synchronization */
1226
        apr_thread_mutex_lock(child_lock);       
1227
        if (s_nAsyncHandlingThreadsInit >= g_DedicatedThreadsForAsyncCallbacks){
1228
            tls_hIOCP = ThreadDispatchIOCP;
1229
        }
1230
        else{
1231
            tls_hIOCP = AsyncDispatchIOCP;
1232
            s_nAsyncHandlingThreadsInit++;
1233
            AP_LOG_ERROR(APLOG_MARK,APLOG_NOTICE, 0, ap_server_conf,
1234
                         "Thread %04d dedicated for Async completion callbacks.",
1235
                         GetCurrentThreadId());
1236
        }
1237
        apr_thread_mutex_unlock(child_lock);
1238
    }
1239
1240
    /* Start the worker thread loop */
743
    while (1) {
1241
    while (1) {
744
        conn_rec *c;
1242
        conn_rec *c;
745
        apr_int32_t disconnected;
1243
        apr_int32_t disconnected;
Lines 768-795 Link Here
768
        }
1266
        }
769
1267
770
        ap_create_sb_handle(&sbh, context->ptrans, 0, thread_num);
1268
        ap_create_sb_handle(&sbh, context->ptrans, 0, thread_num);
1269
1270
        /* LTAC: If Async context was retrieved process it.
1271
         */
1272
        if (context->pAsyncConRec != NULL){
1273
            HandlePendingContext(context, sbh);
1274
            continue;
1275
        }
1276
771
        c = ap_run_create_connection(context->ptrans, ap_server_conf,
1277
        c = ap_run_create_connection(context->ptrans, ap_server_conf,
772
                                     context->sock, thread_num, sbh,
1278
                                     context->sock, thread_num, sbh,
773
                                     context->ba);
1279
                                     context->ba);
774
1280
775
        if (c) {
1281
        if (c) {
1282
            /* LTAC: Setup IOCP details before processing the connection
1283
             */            
1284
            c->hAsyncDispatchIOCP = AsyncDispatchIOCP;
1285
            c->pOverlapped = &context->Overlapped;   
1286
            c->pContext = context;
1287
            c->pRequest = NULL;
1288
            context->pAsyncConRec = c;
1289
            c->nUsageCount = 0;            
1290
776
            ap_process_connection(c, context->sock);
1291
            ap_process_connection(c, context->sock);
1292
1293
           
1294
            /* LTAC: If connection is set to any of the pending states
1295
             * it represents Async or Long operation, hence continue
1296
             * processing the rest of the connections by not blocking
1297
             * the current thread, once the Async / long operation is 
1298
             * done, winnt_get_connection will get the context and we
1299
             * can clean it up here
1300
             */
1301
            if (use_acceptex &&
1302
                c->opState == CONN_OPSTATE_ASYNC_PRE_READY){
1303
                /* Async operation was spwan by current thread, do the
1304
                 * necessary book keeping tasks and then release the
1305
                 * context lock so that other threads that would process
1306
                 * the Async completion notification can cleanup the 
1307
                 * connection context safely 
1308
                 */
1309
1310
                ap_assert(c->pRequest->the_request != NULL);
1311
                AP_LOG_CERROR(APLOG_MARK,APLOG_INFO, 0, c,
1312
                    "Thread(%04d) Connection(%05d, sock: %05d) Async Initiated!"
1313
                    "URI: %s", GetCurrentThreadId(), c->id, context->
1314
                    accept_socket, c->pRequest->the_request);
1315
1316
                async_context_add(context);
1317
                
1318
                /* Released the connection conext, and set the lock state as 
1319
                 * ready for other threads to process/cleanup 
1320
                 */
1321
                context = NULL;
1322
                apr_atomic_set32((volatile apr_uint32_t*) &c->opState, 
1323
                                 CONN_OPSTATE_ASYNC_READY);                
1324
                continue;
1325
            }
1326
            
1327
            /* LTAC: Connection is not Async, so reset the context state 
1328
             */ 
1329
            context->pAsyncConRec = NULL;
777
            apr_socket_opt_get(context->sock, APR_SO_DISCONNECTED,
1330
            apr_socket_opt_get(context->sock, APR_SO_DISCONNECTED,
778
                               &disconnected);
1331
                               &disconnected);
779
            if (!disconnected) {
1332
            if (!disconnected) {
780
                context->accept_socket = INVALID_SOCKET;
1333
                context->accept_socket = INVALID_SOCKET;
781
                ap_lingering_close(c);
1334
                /* LTAC: Do not call socket lingering close while using 
782
            }
1335
                 * AcceptEx + Keep alive OFF, because the connection and socket
783
            else if (!use_acceptex) {
1336
                 * would get closed any moment and calling lingering close will
784
                /* If the socket is disconnected but we are not using acceptex,
1337
                 * result in crash
785
                 * we cannot reuse the socket. Disconnected sockets are removed
1338
                 */
786
                 * from the apr_socket_t struct by apr_sendfile() to prevent the
1339
                if (!use_acceptex ||
787
                 * socket descriptor from being inadvertently closed by a call
1340
                    c->keepalive == AP_CONN_KEEPALIVE){
788
                 * to apr_socket_close(), so close it directly.
1341
                    /* Call socket lingering close only when Keep alive is ON,
1342
                     * because the connection object would be dead otherwise
1343
                     * and would result in crash while calling lingering close                     
1344
                     */
1345
                    ap_lingering_close(c);                    
1346
                }                                
1347
                /* If socket is connected and uses Acceptex mode, socket gets 
1348
                 * closed during context cleanup                 
789
                 */
1349
                 */
790
                closesocket(context->accept_socket);
791
                context->accept_socket = INVALID_SOCKET;
792
            }
1350
            }
1351
            else {
1352
                /* If the socket is disconnected (by TransmitFile API call) 
1353
                 * irrespective of AcceptEx mode or not we should close the 
1354
                 * socket handle otherwise it would lead to socket handle leak.
1355
                 * Disconnected sockets are removed from the apr_socket_t 
1356
                 * struct by apr_sendfile() to prevent the socket descriptor 
1357
                 * from being inadvertently closed by a call to 
1358
                 * apr_socket_close(), so close it directly.
1359
                 */
1360
                closesocket(context->accept_socket);                
1361
                context->accept_socket = INVALID_SOCKET;                
1362
            }            
793
        }
1363
        }
794
        else {
1364
        else {
795
            /* ap_run_create_connection closes the socket on failure */
1365
            /* ap_run_create_connection closes the socket on failure */
Lines 850-856 Link Here
850
        /* Now start a thread per listener */
1420
        /* Now start a thread per listener */
851
        for (lr = ap_listeners; lr; lr = lr->next) {
1421
        for (lr = ap_listeners; lr; lr = lr->next) {
852
            if (lr->sd != NULL) {
1422
            if (lr->sd != NULL) {
853
                _beginthreadex(NULL, 1000, winnt_accept,
1423
                _beginthreadex(NULL, 1000, winnt_acceptIocp,
854
                               (void *) lr, 0, &tid);
1424
                               (void *) lr, 0, &tid);
855
            }
1425
            }
856
        }
1426
        }
Lines 913-923 Link Here
913
     * on Windows NT/2000
1483
     * on Windows NT/2000
914
     */
1484
     */
915
    if (use_acceptex) {
1485
    if (use_acceptex) {
1486
        /* LTAC: The following variable represents number of CPUs present in 
1487
         * the system, i.e., max number of threads that can be truly executed
1488
         * in concurrent fashion. I believe this should be number of Physical
1489
         * CPUs rather than number of Logical CPUs (HyperThreading is dumb).
1490
         * TODO: For now I'm setting this value as 4 and has scaled well, we
1491
         * can fine tune this value using Win32 API or NUM_OF_PROCESSORS 
1492
         * env variable
1493
         */
1494
        int nAvailableProcessors = 4;
1495
916
        /* Create the worker thread dispatch IOCP */
1496
        /* Create the worker thread dispatch IOCP */
917
        ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
1497
        ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
918
                                                    NULL,
1498
                                                    NULL,
919
                                                    0,
1499
                                                    0,
920
                                                    0); /* CONCURRENT ACTIVE THREADS */
1500
                                                    nAvailableProcessors); /* CONCURRENT ACTIVE THREADS */
1501
1502
1503
        /* If dedicated Async threads are required - then create seperate IO completion port*/
1504
        if (g_DedicatedThreadsForAsyncCallbacks > 0){
1505
            AsyncDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
1506
                                                       NULL,
1507
                                                       0,
1508
                                                       nAvailableProcessors); /* CONCURRENT ACTIVE THREADS */             
1509
        }
1510
        else{
1511
            AsyncDispatchIOCP = ThreadDispatchIOCP;
1512
        }
1513
1514
        AP_LOG_ERROR(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
1515
            "Child %d: Completion Ports - ConnectionRequest: %08x"
1516
            " Async completion callbacks: %08x", my_pid, ThreadDispatchIOCP,
1517
            AsyncDispatchIOCP);
1518
1519
1520
921
        apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild);
1521
        apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild);
922
        qwait_event = CreateEvent(NULL, TRUE, FALSE, NULL);
1522
        qwait_event = CreateEvent(NULL, TRUE, FALSE, NULL);
923
        if (!qwait_event) {
1523
        if (!qwait_event) {
Lines 1094-1100 Link Here
1094
    else { /* Windows NT/2000 */
1694
    else { /* Windows NT/2000 */
1095
        /* Post worker threads blocked on the ThreadDispatch IOCompletion port */
1695
        /* Post worker threads blocked on the ThreadDispatch IOCompletion port */
1096
        while (g_blocked_threads > 0) {
1696
        while (g_blocked_threads > 0) {
1097
            ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, ap_server_conf,
1697
            AP_LOG_ERROR(APLOG_MARK,APLOG_INFO, APR_SUCCESS, ap_server_conf,
1098
                         "Child %d: %d threads blocked on the completion port", my_pid, g_blocked_threads);
1698
                         "Child %d: %d threads blocked on the completion port", my_pid, g_blocked_threads);
1099
            for (i=g_blocked_threads; i > 0; i--) {
1699
            for (i=g_blocked_threads; i > 0; i--) {
1100
                PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_SHUTDOWN, NULL);
1700
                PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_SHUTDOWN, NULL);
Lines 1103-1113 Link Here
1103
        }
1703
        }
1104
        /* Empty the accept queue of completion contexts */
1704
        /* Empty the accept queue of completion contexts */
1105
        apr_thread_mutex_lock(qlock);
1705
        apr_thread_mutex_lock(qlock);
1706
        if (!use_acceptex) {
1106
        while (qhead) {
1707
        while (qhead) {
1107
            CloseHandle(qhead->Overlapped.hEvent);
1708
            CloseHandle(qhead->Overlapped.hEvent);
1108
            closesocket(qhead->accept_socket);
1709
            closesocket(qhead->accept_socket);
1109
            qhead = qhead->next;
1710
            qhead = qhead->next;
1110
        }
1711
        }
1712
        }
1713
        else {
1714
            // LTAC: Cleanup async contexts
1715
            //
1716
            while (async_context_head)
1717
            {
1718
                closesocket(async_context_head->accept_socket);
1719
                async_context_head = async_context_head->next;
1720
            }
1721
        }
1111
        apr_thread_mutex_unlock(qlock);
1722
        apr_thread_mutex_unlock(qlock);
1112
    }
1723
    }
1113
1724
Lines 1200-1212 Link Here
1200
    apr_thread_mutex_destroy(allowed_globals.jobmutex);
1811
    apr_thread_mutex_destroy(allowed_globals.jobmutex);
1201
    apr_thread_mutex_destroy(child_lock);
1812
    apr_thread_mutex_destroy(child_lock);
1202
1813
1203
    if (use_acceptex) {
1814
    /** LTAC: We use qlock for Async context queue */
1815
    if (use_acceptex) 
1816
    {
1204
        apr_thread_mutex_destroy(qlock);
1817
        apr_thread_mutex_destroy(qlock);
1205
        CloseHandle(qwait_event);
1818
        CloseHandle(qwait_event);
1819
            
1820
        /* Close completion port created for dedicated Async thread 
1821
         * handlers
1822
         */
1823
        if (g_DedicatedThreadsForAsyncCallbacks > 0){            
1824
            CloseHandle(AsyncDispatchIOCP);
1825
    }
1206
    }
1826
    }
1207
1827
1208
    apr_pool_destroy(pchild);
1828
    apr_pool_destroy(pchild);
1209
    CloseHandle(exit_event);
1829
    CloseHandle(exit_event);
1210
}
1830
}
1211
1831
1832
extern PASYNC_CONTEXT async_context_head = NULL;
1833
extern PASYNC_CONTEXT async_context_tail = NULL;
1834
extern volatile int g_AsyncContextCount = 0;
1835
1836
/* async_context_add()
1837
 * Adds the Context object to Async queue
1838
 */
1839
PASYNC_CONTEXT async_context_add(const PASYNC_CONTEXT pAsyncCntxt)
1840
{
1841
    apr_thread_mutex_lock(qlock);
1842
1843
    if (async_context_tail != NULL &&         
1844
        async_context_head != NULL){
1845
            async_context_tail->next = pAsyncCntxt;
1846
            async_context_tail = pAsyncCntxt;            
1847
    }
1848
    else if (async_context_head == NULL){
1849
            async_context_head = pAsyncCntxt;
1850
            async_context_tail = pAsyncCntxt;            
1851
    }    
1852
    async_context_tail->next = NULL;
1853
    apr_thread_mutex_unlock(qlock);
1854
    apr_atomic_inc32(&g_AsyncContextCount);
1855
1856
    AP_LOG_ERROR(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
1857
                 "Thread(%04d) Connection(%05d,%s) AsyncCntxt count: %d",                  
1858
                  GetCurrentThreadId(), 0, 0, g_AsyncContextCount);
1859
                
1860
    return async_context_head;
1861
}
1862
1863
/* async_context_remove
1864
 * Removes the Context object from Async queue, note - we do not free memory
1865
 * here, but free it in mpm_recycle_iocp_completion_context()
1866
 */
1867
PASYNC_CONTEXT async_context_remove(const PASYNC_CONTEXT pAsyncCntxt)
1868
{
1869
    PASYNC_CONTEXT pCur = NULL;
1870
    PASYNC_CONTEXT pPrev =  NULL;
1871
    volatile int bIsRemoved = 0;
1872
1873
    apr_thread_mutex_lock(qlock);
1874
1875
    pCur = async_context_head;
1876
    while(pCur != NULL){
1877
        if (pCur == pAsyncCntxt){
1878
            /* found async context */
1879
1880
            if (pPrev == NULL){
1881
                /* pCur is head node */
1882
                async_context_head = pCur->next;
1883
                if (async_context_head == NULL){
1884
                    /* there are no nodes left in queue */
1885
                    async_context_tail = NULL;
1886
                }     
1887
                bIsRemoved = 1;
1888
                break;
1889
            }
1890
            else {
1891
                pPrev->next = pCur->next;  
1892
                /* if we have removed the tail, reset tail */
1893
                if (async_context_tail == pCur){
1894
                    async_context_tail = pPrev;
1895
                }
1896
                bIsRemoved = 1;
1897
                break;
1898
            }
1899
        }
1900
        pPrev = pCur;
1901
        pCur = pCur->next;
1902
    }
1903
1904
    apr_thread_mutex_unlock(qlock);
1905
    if (bIsRemoved == 1){
1906
        apr_atomic_dec32(&g_AsyncContextCount);
1907
    }
1908
1909
    AP_LOG_ERROR(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
1910
                 "Thread(%04d) Connection(%05d,%s) "
1911
                 " NtAsyncIoContext_Remove() %08x - AsyncCntxt count: %d", 
1912
                 GetCurrentThreadId(), 0,"", pAsyncCntxt, g_AsyncContextCount);
1913
    
1914
    
1915
    return async_context_head;
1916
}
1917
1918
1919
/* HandlePendingContext 
1920
 * 
1921
 * Handles Async or long pending operation completion signal. 
1922
 * For Push connections: we close the socket if it is not already closed, and 
1923
 * cleanup the connection record.
1924
 *
1925
 * TODO items:
1926
 * 
1927
 * For Async Read : Provide the status of read operation?
1928
 */
1929
void HandlePendingContext(PCOMP_CONTEXT pContext, ap_sb_handle_t *pSbh)
1930
{
1931
    int i=0;
1932
    apr_int32_t disconnected;
1933
    conn_rec* pAsyncConRec = pContext->pAsyncConRec;
1934
    char* pszRequestUri = "unknown";
1935
1936
    ap_assert(pAsyncConRec != NULL);
1937
1938
    /* Store the request URI */
1939
    if (pAsyncConRec->pRequest &&
1940
        pAsyncConRec->pRequest->the_request){
1941
            pszRequestUri = pAsyncConRec->pRequest->the_request;
1942
    }
1943
        
1944
    /* LTAC: Update the scoreboard handle in Async context since the current
1945
     * thread will be different from the thread that created the context
1946
     */
1947
    pAsyncConRec->sbh = pSbh;        
1948
    /* TODO: Optimize later, wait for not more than 3 seconds */
1949
    for(;i < 3 && pAsyncConRec->opState != CONN_OPSTATE_ASYNC_READY;i++){
1950
        Sleep(1000); 
1951
        if (i == 2){
1952
            ap_log_cerror(APLOG_MARK, APLOG_NOTICE, 0, pContext->pAsyncConRec,
1953
                "Thread(%04d) Connection(%05d, socket: %05d) Context not "
1954
                "released by Async spawner thread!) ", GetCurrentThreadId(), 
1955
                pAsyncConRec->id, pContext->accept_socket);  
1956
        }
1957
    }
1958
1959
	/* At this point we expect the Async operation spwaner thread to have 
1960
	 * relinquised the context object and set its state to READY, I believe
1961
	 * 3 seconds should be more than enough for relinquishing, if not we will
1962
	 * assert (logs error and restarts Apache). 
1963
	 */
1964
	ap_assert(pAsyncConRec->opState == CONN_OPSTATE_ASYNC_READY);   
1965
    ap_assert(pAsyncConRec->opState != CONN_OPSTATE_ASYNC_DONE);   
1966
1967
    /* TODO: Update the scoreboard status - SEPM does not support this 
1968
     * module for Amber release  
1969
     */
1970
    /* 
1971
        check_pipeline_flush(r);
1972
        ap_update_child_status(r->connection->sbh, SERVER_BUSY_LOG, r);
1973
        ap_run_log_transaction(r);
1974
        if (ap_extended_status)
1975
            ap_time_process_request(r->connection->sbh, STOP_PREQUEST);
1976
    */
1977
    
1978
    
1979
    /* Transmit File ?
1980
     */
1981
    if (pAsyncConRec->AsyncOperation.bAsyncSendFile)
1982
    {        
1983
        DWORD dwBytesWritten = 0;                
1984
1985
        if (!GetOverlappedResult((HANDLE)pContext->accept_socket, &pContext->Overlapped, 
1986
            &dwBytesWritten, TRUE)) {
1987
                AP_LOG_CERROR(APLOG_MARK, APLOG_ERR, apr_get_os_error(), pAsyncConRec,
1988
                    "Thread(%04d) TransmitFile failed, socket: %05d, "
1989
                    "Threads ready: %d, URI: %s", GetCurrentThreadId(), 
1990
                    pContext->accept_socket, g_blocked_threads, pszRequestUri);                                                            
1991
        }
1992
        else{
1993
            AP_LOG_CERROR(APLOG_MARK,APLOG_INFO, 0, pAsyncConRec,
1994
                "Thread(%04d) TransmitFile done, socket: %05d, bytes xfer: %d,"
1995
                "Threads ready: %d, url: %s", GetCurrentThreadId(), 
1996
                pContext->accept_socket, dwBytesWritten, g_blocked_threads, 
1997
                pszRequestUri);                                                
1998
        }
1999
    }
2000
    /* End of Transmit File */
2001
    
2002
    apr_socket_opt_get(pContext->sock, APR_SO_DISCONNECTED,
2003
                       &disconnected);    
2004
    if (!disconnected) {        
2005
        AP_LOG_CERROR(APLOG_MARK, APLOG_INFO, 0, pAsyncConRec,
2006
            "Thread(%04d) Async done, closing socket!: %05d, URI: %s",
2007
            GetCurrentThreadId(), pContext->accept_socket, pszRequestUri);  
2008
        pContext->accept_socket = INVALID_SOCKET;
2009
        ap_lingering_close(pContext->pAsyncConRec);
2010
    }
2011
    else {
2012
        AP_LOG_CERROR(APLOG_MARK, APLOG_NOTICE, 0, pAsyncConRec,
2013
            "Thread(%04d) Async done, socket already closed!: %05d, URI: %s",
2014
            GetCurrentThreadId(), pContext->accept_socket, pszRequestUri);  
2015
     }
2016
     
2017
    /* LTAC: If socket is disconnected are we leaking connection objects ?
2018
     * I doubt this since connection objects are associated to 
2019
     * context objects transaction pool which is getting destroyed
2020
     * during mpm_recycle_iocp_completion_context() invocation
2021
     */
2022
    async_context_remove(pContext);
2023
    apr_atomic_set32((volatile apr_uint32_t*) &pAsyncConRec->opState, 
2024
                     CONN_OPSTATE_ASYNC_DONE);
2025
}
1212
#endif /* def WIN32 */
2026
#endif /* def WIN32 */
(-)httpd-2.2.17_orig/server/mpm/winnt/mpm_default.h (+7 lines)
Lines 51-56 Link Here
51
#define DEFAULT_THREADS_PER_CHILD 64
51
#define DEFAULT_THREADS_PER_CHILD 64
52
#endif
52
#endif
53
53
54
/* Default connections to queue per child.
55
 * Required for Asychronous I/O.
56
 */
57
#ifndef DEFAULT_CONNECTIONS_TO_QUEUE_PER_CHILD
58
#define DEFAULT_CONNECTIONS_TO_QUEUE_PER_CHILD 5000
59
#endif
60
54
/* Max number of child processes allowed.
61
/* Max number of child processes allowed.
55
 */
62
 */
56
#define HARD_SERVER_LIMIT 1
63
#define HARD_SERVER_LIMIT 1
(-)httpd-2.2.17_orig/server/mpm/winnt/mpm_winnt.c (+381 lines)
Lines 69-74 Link Here
69
/* ap_my_generation are used by the scoreboard code */
69
/* ap_my_generation are used by the scoreboard code */
70
ap_generation_t volatile ap_my_generation=0;
70
ap_generation_t volatile ap_my_generation=0;
71
71
72
/* Async configurtation states */
73
int g_DedicatedThreadsForAsyncCallbacks = 0;
74
async_sendfile_config g_AsyncSendFileConfig = {
75
                        OFF, NULL, AP_MIN_ASYNCSENDFILE_SIZE};
76
77
/* Handle to core async filter hook */ 
78
AP_DECLARE_DATA ap_filter_rec_t *ap_core_async_output_filter_handle;
79
72
80
73
/* shared by service.c as global, although
81
/* shared by service.c as global, although
74
 * perhaps it should be private.
82
 * perhaps it should be private.
Lines 182-187 Link Here
182
    }
190
    }
183
    return NULL;
191
    return NULL;
184
}
192
}
193
/* Set Number of connection contexts that are allowed to Queue 
194
*  Syntax:
195
*  ConnectionsToQueuePerChild <number of connections>
196
*                                 +---- Cannot be less than ThreadsPerChild value
197
*/
198
static const char *set_connections_to_queue_per_child (cmd_parms *cmd, void *dummy, const char *arg)
199
{
200
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
201
    if (err != NULL) {
202
        return err;
203
    }
204
205
    g_MaxConnectionsToQueuePerChild = atoi(arg);
206
	ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL,
207
		"INFO: Max connections to queue per child is defined as : %d ", g_MaxConnectionsToQueuePerChild);
208
209
	/* Check if the max connections to queue per child is set less than the threads per child.
210
	 * If so, set at threads per child. 
211
	 */
212
	if (g_MaxConnectionsToQueuePerChild < ap_threads_per_child) {
213
		g_MaxConnectionsToQueuePerChild = ap_threads_per_child;
214
		ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
215
                     "WARNING: Max connections to queue per child cannot be less than threads_per_child. Set to threads_per_child - %d", 
216
					 ap_threads_per_child);
217
	}
218
   
219
    return NULL;
220
}
185
static const char *set_disable_acceptex(cmd_parms *cmd, void *dummy, char *arg)
221
static const char *set_disable_acceptex(cmd_parms *cmd, void *dummy, char *arg)
186
{
222
{
187
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
223
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
Lines 196-201 Link Here
196
    return NULL;
232
    return NULL;
197
}
233
}
198
234
235
/* Number of connection contexts that are allowed to Queue 
236
*  Syntax:
237
*    AsyncSendFile <Off|AnyDirectory|GivenDirectory> 
238
*                     |      |            +--------- Async Write only performed for folders specified by 
239
*                     |      |                       "ForceAsyncSendFile" config item 
240
*                     |      +------ Automatically detects whether Async write can be done based on file size
241
*                     +---- Disable Async Write feature (Default) 
242
*/
243
static const char *set_async_send_file(cmd_parms *cmd, void *dummy, char *arg)
244
{
245
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
246
    if (err != NULL) {
247
        return err;
248
    }
249
    if (strcasecmp(arg, "off") == 0) {
250
        g_AsyncSendFileConfig.mode = OFF;
251
    }
252
    else if (strcasecmp(arg, "givendirectory") == 0) {
253
        g_AsyncSendFileConfig.mode = GIVEN_DIR;
254
    }
255
    else if (strcasecmp(arg, "anydirectory") == 0) {
256
        g_AsyncSendFileConfig.mode = ANY_DIR;        
257
    }
258
259
    AP_LOG_ERROR(APLOG_MARK, APLOG_NOTICE, 0, NULL,
260
        "Child %d: Async SendFile Mode: %d", my_pid, g_AsyncSendFileConfig.mode);
261
    return NULL;
262
}
263
/* Cleanup callback routine for AsyncSendFile configuration Hash we matiain*/
264
static apr_status_t ap_given_dirs_hash_destroy(void *notused)
265
{
266
    g_AsyncSendFileConfig.pGivenDirsHash = NULL;    
267
    return APR_SUCCESS;
268
}
269
/* Force AsyncSend write for file download requests happening from the given folder
270
 * This configuration item will get used only if "AsyncSendFile: is set to GivenDirectory"
271
 * Syntax:
272
 *    ForceAsyncSendFile <Absolute folder path 1>
273
 *    ForceAsyncSendFile <Absolute folder path 2>
274
 *    ...
275
 *    ..
276
 */
277
static const char *set_force_async_sendfile(cmd_parms *cmd, void *dummy, char *arg)
278
{
279
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
280
    if (err != NULL) {
281
        return err;
282
    }
283
    if (g_AsyncSendFileConfig.pGivenDirsHash == NULL){
284
        /* LTAC: Setup Hash table for Async Send file directories*/
285
        g_AsyncSendFileConfig.pGivenDirsHash = apr_hash_make(cmd->pool);
286
        apr_pool_cleanup_register(cmd->pool, NULL,
287
                                  ap_given_dirs_hash_destroy,
288
                                  apr_pool_cleanup_null);
289
    }
290
    
291
    if (arg != NULL){        
292
        char *pszKey = apr_pstrdup(cmd->pool, arg);
293
        unsigned int *pVal = apr_palloc(cmd->pool, sizeof(*pVal));
294
        *pVal = 1;
295
        /* make Directory spec to lower case */
296
        pszKey = strlwr(pszKey);
297
298
        /* Set the hash */
299
        apr_hash_set(g_AsyncSendFileConfig.pGivenDirsHash, pszKey, APR_HASH_KEY_STRING, pVal);
300
    }
301
302
    AP_LOG_ERROR(APLOG_MARK, APLOG_NOTICE, 0, NULL,
303
        "Child %d: Forcing async send file operation for %s", my_pid,
304
        arg);
305
306
    return NULL;
307
}
308
/* AsyncSend write will get triggered only if the download file size is more or equal to the 
309
 * size mentioned in this configuration item 
310
 * Syntax:
311
 *    AsyncSendFileMinSize <size in bytes>   
312
 *                            +----  Defaults to AP_MIN_ASYNCSENDFILE_SIZE (1MB) if specified size is less than 1MB
313
 */
314
static const char *set_async_sendfile_minsize(cmd_parms *cmd, void *dummy, char *arg)
315
{
316
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
317
    if (err != NULL) {
318
        return err;
319
    }
320
    
321
    if (arg != NULL){
322
        g_AsyncSendFileConfig.ullMinFileSizeForAsyncSend = (ULONGLONG) _atoi64(arg);
323
324
        if (g_AsyncSendFileConfig.ullMinFileSizeForAsyncSend < AP_MIN_ASYNCSENDFILE_SIZE) {
325
            g_AsyncSendFileConfig.ullMinFileSizeForAsyncSend = AP_MIN_ASYNCSENDFILE_SIZE;
326
            AP_LOG_ERROR(APLOG_MARK, APLOG_WARNING, 0, NULL,
327
                "Child %d: Async Send Minimum file size is set low: %s Bytes, "
328
                "defaulting to stock value", my_pid, arg);            
329
        }        
330
    }
331
332
    AP_LOG_ERROR(APLOG_MARK, APLOG_NOTICE, 0, NULL,
333
                "Child %d: Async Send Minimum file size set to %d bytes", my_pid,
334
                g_AsyncSendFileConfig.ullMinFileSizeForAsyncSend );    
335
336
    return NULL;
337
}
338
/* Experimental configuration item, can be used when Async callback notifications are not getting
339
 * fair chance to be picked up by worker threads
340
 * Syntax:
341
 *   DedicatedThreadsForAsyncCallbacks <number of Threads>
342
 *                                           +--- Cannot be more than half the ThreadsPerChild value
343
 */      
344
static const char *set_dedicated_threads_for_async_callbacks(cmd_parms *cmd, void *dummy, char *arg)
345
{
346
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
347
    if (err != NULL) {
348
        return err;
349
    }
350
    g_DedicatedThreadsForAsyncCallbacks = atoi(arg);
351
    if (g_DedicatedThreadsForAsyncCallbacks > ap_threads_per_child/2) {
352
        AP_LOG_ERROR(APLOG_MARK, APLOG_WARNING, 0, NULL,
353
                     "Child %d: Dedicated threads for Asynchronous callbacks cannot be "
354
                     "more than half the number of Worker threads", my_pid);
355
        g_DedicatedThreadsForAsyncCallbacks = ap_threads_per_child/2;
356
    }
357
358
    AP_LOG_ERROR(APLOG_MARK, APLOG_NOTICE, 0, NULL,
359
        "Child %d: Dedicated threads for Asynchronous callbacks :%d", my_pid,
360
        g_DedicatedThreadsForAsyncCallbacks);
361
362
    return NULL;
363
}
364
365
366
367
368
199
static const command_rec winnt_cmds[] = {
369
static const command_rec winnt_cmds[] = {
200
LISTEN_COMMANDS,
370
LISTEN_COMMANDS,
201
AP_INIT_TAKE1("ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF,
371
AP_INIT_TAKE1("ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF,
Lines 204-209 Link Here
204
  "Maximum worker threads in a server for this run of Apache"),
374
  "Maximum worker threads in a server for this run of Apache"),
205
AP_INIT_NO_ARGS("Win32DisableAcceptEx", set_disable_acceptex, NULL, RSRC_CONF,
375
AP_INIT_NO_ARGS("Win32DisableAcceptEx", set_disable_acceptex, NULL, RSRC_CONF,
206
  "Disable use of the high performance AcceptEx WinSock2 API to work around buggy VPN or Firewall software"),
376
  "Disable use of the high performance AcceptEx WinSock2 API to work around buggy VPN or Firewall software"),
377
/** Async specific settings */
378
AP_INIT_TAKE1("ConnectionsToQueuePerChild", set_connections_to_queue_per_child, NULL, RSRC_CONF, 
379
  "Maximum number of connections to queue per child"),
380
AP_INIT_TAKE1("AsyncSendFile", set_async_send_file, NULL, RSRC_CONF, 
381
  "True asynchronous send file feature"),
382
AP_INIT_TAKE1("ForceAsyncSendFile", set_force_async_sendfile,
383
  NULL, RSRC_CONF, "Enforce async send file be used for given directory, this item can be overridden by AsyncSendFile AnyDir setting"),
384
AP_INIT_TAKE1("AsyncSendFileMinSize", set_async_sendfile_minsize,
385
  NULL, RSRC_CONF, "Enforce async send file be done only if the file size of the download request is equal or more than the given one"),
386
AP_INIT_TAKE1("DedicatedThreadsForAsyncCallbacks", set_dedicated_threads_for_async_callbacks,
387
  NULL, RSRC_CONF, "Experimental: Allocate dedicated number of threads for Async completion callback processing"),
388
207
389
208
{ NULL }
390
{ NULL }
209
};
391
};
Lines 1439-1444 Link Here
1439
1621
1440
    ap_listen_pre_config();
1622
    ap_listen_pre_config();
1441
    ap_threads_per_child = DEFAULT_THREADS_PER_CHILD;
1623
    ap_threads_per_child = DEFAULT_THREADS_PER_CHILD;
1624
	g_MaxConnectionsToQueuePerChild = DEFAULT_CONNECTIONS_TO_QUEUE_PER_CHILD;
1442
    ap_pid_fname = DEFAULT_PIDLOG;
1625
    ap_pid_fname = DEFAULT_PIDLOG;
1443
    ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
1626
    ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
1444
#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
1627
#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
Lines 1741-1746 Link Here
1741
    return 0; /* Restart */
1924
    return 0; /* Restart */
1742
}
1925
}
1743
1926
1927
/* LTAC: Core async filter function responsible to idenfity whether a file download 
1928
 * request can be handled asynchronously 
1929
 *
1930
 * We allow Async file download only if following rules are met:
1931
 *
1932
 *  1. End Of Connection bucket present
1933
 *  2. Only one file download request is present
1934
 *  3. Transfer size > AsyncSendFileMinSize (10KB by default) 
1935
 */ 
1936
apr_status_t ap_core_winnt_async_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
1937
{
1938
    conn_rec *c = f->c;        
1939
    request_rec* r = f->r;
1940
    apr_bucket_brigade *bb = b;
1941
    core_winnt_async_ctx* pCtx = f->ctx;
1942
    
1943
    do{        
1944
        apr_bucket *e = NULL; 
1945
        apr_bucket *firstfilebucket = NULL;
1946
1947
        if (g_AsyncSendFileConfig.mode == OFF ||
1948
            c == NULL ||
1949
            r == NULL || 
1950
            r->filename == NULL ||
1951
            r->chunked || /*Don't do Async transfer in chunked mode responses*/
1952
            c->hAsyncDispatchIOCP == NULL){
1953
            break;
1954
        }
1955
1956
        if (g_AsyncSendFileConfig.mode == GIVEN_DIR){
1957
1958
            /* Check whether download request folders match wiht the given 
1959
             * directories
1960
             */
1961
            unsigned char* pValue=0;            
1962
            char szFolder[MAX_PATH*2]; /* For now we support only 520 bytes*/
1963
            char* pszFileNameStartPos = strrchr(r->filename, '/');
1964
            int nFolderLen = pszFileNameStartPos - r->filename;
1965
1966
            if (pszFileNameStartPos == NULL){           
1967
                /* Cannot identify the filename or directory hence pass 
1968
                 * request to next filter 
1969
                 */
1970
                break;
1971
            }
1972
1973
            strncpy(szFolder, r->filename, nFolderLen);                
1974
            szFolder[nFolderLen] = 0;
1975
            strlwr(szFolder);
1976
            
1977
            pValue = apr_hash_get(g_AsyncSendFileConfig.pGivenDirsHash, szFolder, APR_HASH_KEY_STRING);
1978
            if (pValue != NULL){                
1979
                 AP_LOG_CERROR(APLOG_MARK, APLOG_DEBUG, 0, c,
1980
                               "Thread(%04d) Dir matched for Async File download, Request: %s", 
1981
                                GetCurrentThreadId(), r->unparsed_uri);    
1982
            }
1983
            else { 
1984
                /* Cannot identify the directory as one of the conf provided 
1985
                 * one, so pass down the request to next filter
1986
                 */
1987
                break;
1988
            }
1989
        }
1990
        else if (g_AsyncSendFileConfig.mode != ANY_DIR){
1991
            /* Async Send File not configured even for Automatic mode, so 
1992
             * delegate the brigade to next filter 
1993
             */
1994
            break;
1995
        }
1996
    
1997
        ap_assert(g_AsyncSendFileConfig.mode != OFF);
1998
        /* LTAC: Automatically idenfity whether we are just sending only 1 File over network
1999
         * pipe and if so Enable AsyncSendFile 
2000
         */
2001
        if (pCtx == NULL){
2002
            pCtx = apr_pcalloc(r->pool, sizeof(*pCtx));
2003
            pCtx->Flags.nAll = 0;
2004
            pCtx->nFilesToSend = 0;
2005
            pCtx->ullFileSize = 0;
2006
        }
2007
2008
        if (bb && !APR_BRIGADE_EMPTY(bb)) {            
2009
            
2010
            for (e = APR_BRIGADE_FIRST(bb);
2011
                 e != APR_BRIGADE_SENTINEL(bb);
2012
                 e = APR_BUCKET_NEXT(e)) {
2013
        
2014
                /* LTAC: Check whether a file bucket was already not present
2015
                 * and current bucket is a file 
2016
                 */
2017
                if (APR_BUCKET_IS_FILE(e)) {        
2018
                    
2019
                    /* We keep note of the file size to take decisions later 
2020
                     * whether Async is really required for current download 
2021
                     * request 
2022
                     */
2023
                    apr_bucket_file *f = e->data;
2024
                    pCtx->ullFileSize += e->length;
2025
                    
2026
                    /* Same file could have been broken in to multiple buckets,
2027
                     * so handle it*/
2028
                    if (pCtx->fd == NULL){                        
2029
                        /* Store the file descriptor, to identify multiple
2030
                         * file download request coming in same brigade, this 
2031
                         * could be highly unlikely but since any module can 
2032
                         * abuse the buckets we proactively check this 
2033
                         * condition and bypass Async download feature 
2034
                         */
2035
                        pCtx->fd = f->fd;
2036
                        pCtx->nFilesToSend++;
2037
                    }
2038
                    else if (pCtx->fd != f->fd){                                                 
2039
                        /* Don't continue further since more than one file 
2040
                         * download request is present in the brigade 
2041
                         */
2042
                        pCtx->nFilesToSend++;                        
2043
                        ap_assert(pCtx->nFilesToSend > 1);
2044
                        break;
2045
                    }
2046
                }
2047
                else if (APR_BUCKET_IS_EOS(e)){
2048
                    /* End of Stream bucket identified, record it */
2049
                    pCtx->Flags.bSeenEOS = 1;
2050
                }
2051
                else if (AP_BUCKET_IS_EOC(e)){                
2052
                    /* End of connection bucket is present, record it and 
2053
                     * break out of the loop since this is the last bucket
2054
                     * in the brigade to do Async Transmit file 
2055
                     */
2056
                    pCtx->Flags.bSeenEOC = 1;
2057
                    //break;
2058
                }
2059
            }
2060
        }
2061
2062
        /*AP_LOG_RERROR(APLOG_MARK, APLOG_DEBUG, 0, r,
2063
            "Thread(%04d) File download %d, flags: 0x%04x", GetCurrentThreadId(), 
2064
            pCtx->nFilesToSend, pCtx->Flags);    */        
2065
2066
2067
        /* Allow Async file download only if following rules are met
2068
         *  1. End Of Connection bucket present
2069
         *  2. Only one file download request is present
2070
         *  3. Transfer size >10KB : TODO - make file size configurable
2071
         */
2072
  
2073
        if(!pCtx->Flags.bSeenEOC ||  
2074
           pCtx->nFilesToSend !=1 ||  
2075
           pCtx->ullFileSize < g_AsyncSendFileConfig.ullMinFileSizeForAsyncSend){
2076
                break;
2077
        }
2078
        
2079
        /* At this point all rules are satisfied, remove dupe file buckets 
2080
         * withing the brigade if any. This can happen if the file size is
2081
         * larger than 16MB, refer core.c for details
2082
         */
2083
        for (e = APR_BRIGADE_FIRST(bb);
2084
             e != APR_BRIGADE_SENTINEL(bb);
2085
             e = APR_BUCKET_NEXT(e)) {                
2086
            
2087
            /* LTAC: Check whether a file bucket was already not present
2088
             * and current bucket is a file
2089
             */ 
2090
            if (APR_BUCKET_IS_FILE(e)) {                        
2091
                if (firstfilebucket == NULL){
2092
                    firstfilebucket = e;
2093
                }
2094
                else {
2095
                    /* There are multiple buckets for same file, combine into one
2096
                     * Note: e->start cannot be zero for Range: bytes specification
2097
                     */ 
2098
                    firstfilebucket->length += e->length;
2099
                    apr_bucket_delete(e);
2100
                }
2101
            }
2102
        }        
2103
        
2104
        /* All prep done for Async send file request
2105
         * Setup the async send file state flags 
2106
         */        
2107
        c->AsyncOperation.bAsyncSendFile = 1;
2108
2109
        AP_LOG_CERROR(APLOG_MARK, APLOG_DEBUG, 0, c,
2110
            "Thread(%04d) Automatic TF File download enabled. Request: %s", 
2111
            GetCurrentThreadId(), r->unparsed_uri);      
2112
2113
    }while(0);
2114
    
2115
    return ap_pass_brigade(f->next, b);
2116
}
2117
1744
static void winnt_hooks(apr_pool_t *p)
2118
static void winnt_hooks(apr_pool_t *p)
1745
{
2119
{
1746
    /* The prefork open_logs phase must run before the core's, or stderr
2120
    /* The prefork open_logs phase must run before the core's, or stderr
Lines 1753-1758 Link Here
1753
    ap_hook_post_config(winnt_post_config, NULL, NULL, 0);
2127
    ap_hook_post_config(winnt_post_config, NULL, NULL, 0);
1754
    ap_hook_child_init(winnt_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2128
    ap_hook_child_init(winnt_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1755
    ap_hook_open_logs(winnt_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE);
2129
    ap_hook_open_logs(winnt_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE);
2130
2131
    /* Hook to identify whether AsyncSendFile can be done ? */    
2132
    ap_core_async_output_filter_handle =
2133
        ap_register_output_filter("ASYNC_CORE", ap_core_winnt_async_output_filter,
2134
                                  NULL, AP_FTYPE_PROTOCOL);
2135
2136
 
1756
}
2137
}
1757
2138
1758
AP_MODULE_DECLARE_DATA module mpm_winnt_module = {
2139
AP_MODULE_DECLARE_DATA module mpm_winnt_module = {
(-)httpd-2.2.17_orig/server/mpm/winnt/mpm_winnt.h (-1 / +67 lines)
Lines 26-31 Link Here
26
#define APACHE_MPM_WINNT_H
26
#define APACHE_MPM_WINNT_H
27
27
28
#include "ap_listen.h"
28
#include "ap_listen.h"
29
#include "apr_hash.h"
29
30
30
/* From service.c: */
31
/* From service.c: */
31
32
Lines 113-130 Link Here
113
    apr_pool_t *ptrans;
114
    apr_pool_t *ptrans;
114
    apr_bucket_alloc_t *ba;
115
    apr_bucket_alloc_t *ba;
115
    short socket_family;
116
    short socket_family;
117
    
118
    // LTAC: Persist the connection record if we are doing Async I/O
119
    //
120
    conn_rec*   pAsyncConRec;    
116
} COMP_CONTEXT, *PCOMP_CONTEXT;
121
} COMP_CONTEXT, *PCOMP_CONTEXT;
117
122
123
// For Push + Async I/O framework
124
typedef PCOMP_CONTEXT PASYNC_CONTEXT;  
125
126
PASYNC_CONTEXT async_context_head;
127
PASYNC_CONTEXT async_context_tail;
128
volatile int g_AsyncContextCount;
129
int g_MaxConnectionsToQueuePerChild;
130
131
PASYNC_CONTEXT async_context_add(const PASYNC_CONTEXT pAsyncCntxt);
132
PASYNC_CONTEXT async_context_remove(const PASYNC_CONTEXT pAsyncCntxt);
133
118
typedef enum {
134
typedef enum {
119
    IOCP_CONNECTION_ACCEPTED = 1,
135
    IOCP_CONNECTION_ACCEPTED = 1,
120
    IOCP_WAIT_FOR_RECEIVE = 2,
136
    IOCP_WAIT_FOR_RECEIVE = 2,
121
    IOCP_WAIT_FOR_TRANSMITFILE = 3,
137
    IOCP_WAIT_FOR_TRANSMITFILE = 3,
122
    IOCP_SHUTDOWN = 4
138
    IOCP_SHUTDOWN = 4,
139
140
    /* LTAC (L.Thangaraj AntonyCrouse) Async feature */
141
    IOCP_CONNECTION_PENDING_DONE =100,
142
143
    /* LTAC: Async file write feature */
144
    IOCP_TRANSMITFILE_DONE =101  
123
} io_state_e;
145
} io_state_e;
124
146
125
PCOMP_CONTEXT mpm_get_completion_context(void);
147
PCOMP_CONTEXT mpm_get_completion_context(void);
126
void          mpm_recycle_completion_context(PCOMP_CONTEXT pCompContext);
148
void          mpm_recycle_completion_context(PCOMP_CONTEXT pCompContext);
127
apr_status_t  mpm_post_completion_context(PCOMP_CONTEXT pCompContext, io_state_e state);
149
apr_status_t  mpm_post_completion_context(PCOMP_CONTEXT pCompContext, io_state_e state);
128
void hold_console_open_on_error(void);
150
void hold_console_open_on_error(void);
151
void          mpm_recycle_iocp_completion_context(PCOMP_CONTEXT pCompContext);
152
153
/* Async Send File mode */ 
154
typedef enum {
155
    OFF = 0,
156
    GIVEN_DIR, /* only work on specified folders */ 
157
    ANY_DIR     /* automatically detect if Async send can be done - experimental*/
158
} async_sendfile_mode_e;
159
160
/* Apache Async send file configuration */
161
#define AP_MIN_ASYNCSENDFILE_SIZE  (1*1024*1024)       /*1 MB*/
162
typedef struct {
163
    /* Specifies the Async file mode - Off|Forced|Auto */
164
    async_sendfile_mode_e mode;
165
166
    /* Contains the folder details for Forced mode */
167
    apr_hash_t*           pGivenDirsHash;
168
169
    /* Download file size that must be satisfied in order for the download 
170
     * request to be served using Async write */
171
    ULONGLONG             ullMinFileSizeForAsyncSend;
172
} async_sendfile_config;
173
174
extern async_sendfile_config g_AsyncSendFileConfig;
175
176
/* Experimental: Number of threads dedicate to process only Async completion
177
 * notifications.
178
 */
179
extern int g_DedicatedThreadsForAsyncCallbacks;
180
181
/* Async filter context structure */ 
182
typedef struct _core_winnt_async_ctx {
183
    int         nFilesToSend;
184
    ULONGLONG   ullFileSize;
185
    apr_file_t  *fd;
186
    union {
187
            int bSeenEOC:1;
188
            int bSeenEOS:1;
189
            int bSeenFlush:1;
190
            int nAll;
191
    } Flags;
192
193
} core_winnt_async_ctx;
194
129
#endif /* APACHE_MPM_WINNT_H */
195
#endif /* APACHE_MPM_WINNT_H */
130
/** @} */
196
/** @} */
(-)httpd-2.2.17_orig/server/protocol.c (+5 lines)
Lines 885-890 Link Here
885
     */
885
     */
886
    r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
886
    r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
887
887
888
    /* LTAC: For Async I/O
889
     */
890
    r->nLastAsyncOperation = 0;
891
    memset(&conn->AsyncOperation, 0, sizeof(conn->AsyncOperation));
892
888
    tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
893
    tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
889
894
890
    /* Get the request... */
895
    /* Get the request... */
(-)httpd-2.2.17_orig/srclib/apr/include/apr_network_io.h (+28 lines)
Lines 567-572 Link Here
567
                                              apr_size_t *len,
567
                                              apr_size_t *len,
568
                                              apr_int32_t flags);
568
                                              apr_int32_t flags);
569
569
570
#ifdef _WIN32
571
/**
572
 * Send a file from an open file descriptor to a socket, along with 
573
 * optional headers in true Async Fashion
574
 * @param sock The socket to which we're writing
575
 * @param file The open file from which to read
576
 * @param hdtr A structure containing the headers and trailers to send
577
 * @param offset Offset into the file where we should begin writing
578
 * @param len (input)  - Number of bytes to send from the file 
579
 *            (output) - Number of bytes actually sent, 
580
 *                       including headers, file, and trailers
581
 * @param flags APR flags that are mapped to OS specific flags
582
 * @HANDLE hAsyncDispatchIOCP handle to the IOCP used for Async completion
583
 *                            notification  
584
 * @remark This functions transmits file over socket is a non-blocking manner. 
585
 * The send file request status is known immediately by return value.
586
 * Once file transfer is done, the Async framework gets the status of 
587
 * completion, error during transmission is also notified to Async framework.
588
 */
589
APR_DECLARE(apr_status_t) apr_socket_sendfileEx(apr_socket_t *sock, 
590
                                                apr_file_t *file,
591
                                                apr_hdtr_t *hdtr,
592
                                                apr_off_t *offset,
593
                                                apr_size_t *len,
594
                                                apr_int32_t flags,
595
                                                HANDLE hAsyncDispatchIOCP);
596
597
#endif /* _WIN32 */
570
#endif /* APR_HAS_SENDFILE */
598
#endif /* APR_HAS_SENDFILE */
571
599
572
/**
600
/**
(-)httpd-2.2.17_orig/srclib/apr/network_io/win32/sendrecv.c (+150 lines)
Lines 454-458 Link Here
454
    return status;
454
    return status;
455
}
455
}
456
456
457
#ifdef _WIN32
458
459
/* Redefining IOCP_TRANSMITFILE_DONE since including mpm_winnt.h opens up 
460
 * too many unwanted include dependencies for this file under APR library
461
 */
462
#define IOCP_TRANSMITFILE_DONE 101
463
464
/* Async send file implementation
465
 */
466
APR_DECLARE(apr_status_t) apr_socket_sendfileEx(apr_socket_t *sock, 
467
                                                apr_file_t *file,
468
                                                apr_hdtr_t *hdtr,
469
                                                apr_off_t *offset,
470
                                                apr_size_t *len,
471
                                                apr_int32_t flags,
472
                                                HANDLE hAsyncDispatchIOCP) 
473
{
474
    apr_status_t status = APR_SUCCESS;
475
    apr_status_t rv;
476
    apr_off_t curoff = *offset;
477
    DWORD dwFlags = 0;
478
    apr_size_t nbytes;
479
    TRANSMIT_FILE_BUFFERS tfb, *ptfb = NULL;
480
    int ptr = 0;
481
    apr_size_t bytes_to_send;   /* Bytes to send out of the file (not including headers) */    
482
    int sendv_trailers = 0;     // LTAC TODO: remove
483
    char hdtrbuf[4096];
484
485
    if (apr_os_level < APR_WIN_NT) {
486
        return APR_ENOTIMPL;
487
    }
488
489
    /* Use len to keep track of number of total bytes sent (including headers) */
490
    bytes_to_send = *len;
491
    *len = 0;
492
493
    /* Handle the goofy case of sending headers/trailers and a zero byte file */
494
    if (!bytes_to_send && hdtr) {
495
        if (hdtr->numheaders) {
496
            rv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders, 
497
                                  &nbytes);
498
            if (rv != APR_SUCCESS)
499
                return rv;
500
            *len += nbytes;
501
        }
502
        if (hdtr->numtrailers) {
503
            rv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers,
504
                                  &nbytes);
505
            if (rv != APR_SUCCESS)
506
                return rv;
507
            *len += nbytes;
508
        }
509
        return APR_SUCCESS;
510
    }
511
512
    memset(&tfb, '\0', sizeof (tfb));
513
514
    
515
    /* Collapse the headers into a single buffer */
516
    if (hdtr && hdtr->numheaders) {
517
        apr_size_t head_length = tfb.HeadLength;
518
        ptfb = &tfb;
519
        nbytes = 0;
520
        rv = collapse_iovec((char **)&ptfb->Head, &head_length, 
521
                            hdtr->headers, hdtr->numheaders, 
522
                            hdtrbuf, sizeof(hdtrbuf));
523
524
        tfb.HeadLength = (DWORD)head_length;
525
526
        /* If not enough buffer, punt to sendv */
527
        if (rv == APR_INCOMPLETE) {
528
            rv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders, &nbytes);
529
            if (rv != APR_SUCCESS)
530
                return rv;
531
            *len += nbytes;
532
            ptfb = NULL;
533
        }
534
    }
535
536
    /* Initialize the overlapped structure used on TransmitFile
537
     */
538
    if (!sock->overlapped) {     
539
        /* LTAC: Overlapped structures are required for completion ports use */
540
        return WSAEWOULDBLOCK;
541
    }
542
    if (hAsyncDispatchIOCP)
543
    {
544
        /* Associate socket to Async completion port */
545
        HANDLE hTempCompletion = NULL;
546
        hTempCompletion = CreateIoCompletionPort((HANDLE)sock->socketdes, 
547
                           hAsyncDispatchIOCP, IOCP_TRANSMITFILE_DONE, 0);
548
        if (hTempCompletion == NULL){
549
            return ERROR_UNKNOWN_PORT;            
550
        }
551
    }
552
    if (bytes_to_send) {
553
        DWORD xmitbytes;
554
555
            /* LTAC: We xmit all the contents of files in one shot */
556
            xmitbytes = (DWORD)bytes_to_send;
557
            /* Collapse the trailers into a single buffer */
558
            if (hdtr && hdtr->numtrailers) {
559
                apr_size_t tail_length = tfb.TailLength;
560
                ptfb = &tfb;
561
                rv = collapse_iovec((char**) &ptfb->Tail, &tail_length,
562
                                    hdtr->trailers, hdtr->numtrailers,
563
                                    hdtrbuf + ptfb->HeadLength,
564
                                    sizeof(hdtrbuf) - ptfb->HeadLength);
565
566
                tfb.TailLength = (DWORD)tail_length;
567
568
                if (rv == APR_INCOMPLETE) {
569
                    /* If not enough buffer, punt to sendv, later */
570
                    sendv_trailers = 1;
571
                }
572
            }
573
            /* Do not set disconnect socket flag, we disconnect 
574
               in Async completion callbacks */
575
            
576
            /* Allow system threads to be used for file transfer*/             
577
            dwFlags = TF_USE_KERNEL_APC;     
578
            
579
580
        sock->overlapped->Offset = (DWORD)(curoff);
581
#if APR_HAS_LARGE_FILES
582
        sock->overlapped->OffsetHigh = (DWORD)(curoff >> 32);
457
#endif
583
#endif
584
        /* XXX BoundsChecker claims dwFlags must not be zero. */
585
        rv = TransmitFile(sock->socketdes,  /* socket */
586
                          file->filehand, /* open file descriptor of the file to be sent */
587
                          xmitbytes,      /* number of bytes to send. 0=send all */
588
                          0,              /* Number of bytes per send. 0=use default */
589
                          sock->overlapped,    /* OVERLAPPED structure */
590
                          ptfb,           /* header and trailer buffers */
591
                          dwFlags);       /* flags to control various aspects of TransmitFile */
592
        if (!rv) {
593
            status = apr_get_netos_error();
594
            if ((status == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) ||
595
                (status == APR_FROM_OS_ERROR(WSA_IO_PENDING))) 
596
            {
597
                return ERROR_IO_PENDING;
598
            }
599
        }        
600
    }
601
602
    return status;
603
}
604
605
#endif /* _WIN32 */
606
607
#endif //APR_HAS_SENDFILE
458
608

Return to bug 51019