17
17
18
18
static struct oidset gh_client__oidset_queued = OIDSET_INIT ;
19
19
static unsigned long gh_client__oidset_count ;
20
- static int gh_client__includes_immediate ;
21
20
22
21
struct gh_server__process {
23
22
struct subprocess_entry subprocess ; /* must be first */
@@ -28,13 +27,20 @@ static int gh_server__subprocess_map_initialized;
28
27
static struct hashmap gh_server__subprocess_map ;
29
28
static struct object_directory * gh_client__chosen_odb ;
30
29
31
- #define CAP_GET (1u<<1)
30
+ /*
31
+ * The "objects" capability has 2 verbs: "get" and "post".
32
+ */
33
+ #define CAP_OBJECTS (1u<<1)
34
+ #define CAP_OBJECTS_NAME "objects"
35
+
36
+ #define CAP_OBJECTS__VERB_GET1_NAME "get"
37
+ #define CAP_OBJECTS__VERB_POST_NAME "post"
32
38
33
39
static int gh_client__start_fn (struct subprocess_entry * subprocess )
34
40
{
35
41
static int versions [] = {1 , 0 };
36
42
static struct subprocess_capability capabilities [] = {
37
- { "get" , CAP_GET },
43
+ { CAP_OBJECTS_NAME , CAP_OBJECTS },
38
44
{ NULL , 0 }
39
45
};
40
46
@@ -46,14 +52,16 @@ static int gh_client__start_fn(struct subprocess_entry *subprocess)
46
52
}
47
53
48
54
/*
49
- * Send:
55
+ * Send the queued OIDs in the OIDSET to gvfs-helper for it to
56
+ * fetch from the cache-server or main Git server using "/gvfs/objects"
57
+ * POST semantics.
50
58
*
51
- * get LF
59
+ * objects.post LF
52
60
* (<hex-oid> LF)*
53
61
* <flush>
54
62
*
55
63
*/
56
- static int gh_client__get__send_command (struct child_process * process )
64
+ static int gh_client__send__objects_post (struct child_process * process )
57
65
{
58
66
struct oidset_iter iter ;
59
67
struct object_id * oid ;
@@ -64,7 +72,9 @@ static int gh_client__get__send_command(struct child_process *process)
64
72
* so that we don't have to.
65
73
*/
66
74
67
- err = packet_write_fmt_gently (process -> in , "get\n" );
75
+ err = packet_write_fmt_gently (
76
+ process -> in ,
77
+ (CAP_OBJECTS_NAME "." CAP_OBJECTS__VERB_POST_NAME "\n" ));
68
78
if (err )
69
79
return err ;
70
80
@@ -83,6 +93,46 @@ static int gh_client__get__send_command(struct child_process *process)
83
93
return 0 ;
84
94
}
85
95
96
+ /*
97
+ * Send the given OID to gvfs-helper for it to fetch from the
98
+ * cache-server or main Git server using "/gvfs/objects" GET
99
+ * semantics.
100
+ *
101
+ * This ignores any queued OIDs.
102
+ *
103
+ * objects.get LF
104
+ * <hex-oid> LF
105
+ * <flush>
106
+ *
107
+ */
108
+ static int gh_client__send__objects_get (struct child_process * process ,
109
+ const struct object_id * oid )
110
+ {
111
+ int err ;
112
+
113
+ /*
114
+ * We assume that all of the packet_ routines call error()
115
+ * so that we don't have to.
116
+ */
117
+
118
+ err = packet_write_fmt_gently (
119
+ process -> in ,
120
+ (CAP_OBJECTS_NAME "." CAP_OBJECTS__VERB_GET1_NAME "\n" ));
121
+ if (err )
122
+ return err ;
123
+
124
+ err = packet_write_fmt_gently (process -> in , "%s\n" ,
125
+ oid_to_hex (oid ));
126
+ if (err )
127
+ return err ;
128
+
129
+ err = packet_flush_gently (process -> in );
130
+ if (err )
131
+ return err ;
132
+
133
+ return 0 ;
134
+ }
135
+
86
136
/*
87
137
* Update the loose object cache to include the newly created
88
138
* object.
@@ -131,7 +181,7 @@ static void gh_client__update_packed_git(const char *line)
131
181
}
132
182
133
183
/*
134
- * We expect :
184
+ * Both CAP_OBJECTS verbs return the same format response :
135
185
*
136
186
* <odb>
137
187
* <data>*
@@ -162,7 +212,7 @@ static void gh_client__update_packed_git(const char *line)
162
212
* grouped with a queued request for a blob. The tree-walk *might* be
163
213
* able to continue and let the 404 blob be handled later.
164
214
*/
165
- static int gh_client__get__receive_response (
215
+ static int gh_client__objects__receive_response (
166
216
struct child_process * process ,
167
217
enum gh_client__created * p_ghc ,
168
218
int * p_nr_loose , int * p_nr_packfile )
@@ -241,17 +291,12 @@ static void gh_client__choose_odb(void)
241
291
}
242
292
}
243
293
244
- static int gh_client__get (enum gh_client__created * p_ghc )
294
+ static struct gh_server__process * gh_client__find_long_running_process (
295
+ unsigned int cap_needed )
245
296
{
246
297
struct gh_server__process * entry ;
247
- struct child_process * process ;
248
298
struct strvec argv = STRVEC_INIT ;
249
299
struct strbuf quoted = STRBUF_INIT ;
250
- int nr_loose = 0 ;
251
- int nr_packfile = 0 ;
252
- int err = 0 ;
253
-
254
- trace2_region_enter ("gh-client" , "get" , the_repository );
255
300
256
301
gh_client__choose_odb ();
257
302
@@ -267,6 +312,11 @@ static int gh_client__get(enum gh_client__created *p_ghc)
267
312
268
313
sq_quote_argv_pretty (& quoted , argv .v );
269
314
315
+ /*
316
+ * Find an existing long-running process with the above command
317
+ * line -or- create a new long-running process for this and
318
+ * subsequent 'get' requests.
319
+ */
270
320
if (!gh_server__subprocess_map_initialized ) {
271
321
gh_server__subprocess_map_initialized = 1 ;
272
322
hashmap_init (& gh_server__subprocess_map ,
@@ -280,70 +330,24 @@ static int gh_client__get(enum gh_client__created *p_ghc)
280
330
entry = xmalloc (sizeof (* entry ));
281
331
entry -> supported_capabilities = 0 ;
282
332
283
- err = subprocess_start_strvec (
284
- & gh_server__subprocess_map , & entry -> subprocess , 1 ,
285
- & argv , gh_client__start_fn );
286
- if (err ) {
287
- free (entry );
288
- goto leave_region ;
289
- }
333
+ if (subprocess_start_strvec (& gh_server__subprocess_map ,
334
+ & entry -> subprocess , 1 ,
335
+ & argv , gh_client__start_fn ))
336
+ FREE_AND_NULL (entry );
290
337
}
291
338
292
- process = & entry -> subprocess .process ;
293
-
294
- if (!(CAP_GET & entry -> supported_capabilities )) {
295
- error ("gvfs-helper: does not support GET" );
296
- subprocess_stop (& gh_server__subprocess_map ,
297
- (struct subprocess_entry * )entry );
298
- free (entry );
299
- err = -1 ;
300
- goto leave_region ;
301
- }
302
-
303
- sigchain_push (SIGPIPE , SIG_IGN );
304
-
305
- err = gh_client__get__send_command (process );
306
- if (!err )
307
- err = gh_client__get__receive_response (process , p_ghc ,
308
- & nr_loose , & nr_packfile );
309
-
310
- sigchain_pop (SIGPIPE );
311
-
312
- if (err ) {
339
+ if (entry &&
340
+ (entry -> supported_capabilities & cap_needed ) != cap_needed ) {
341
+ error ("gvfs-helper: does not support needed capabilities" );
313
342
subprocess_stop (& gh_server__subprocess_map ,
314
343
(struct subprocess_entry * )entry );
315
- free (entry );
344
+ FREE_AND_NULL (entry );
316
345
}
317
346
318
- leave_region :
319
347
strvec_clear (& argv );
320
348
strbuf_release (& quoted );
321
349
322
- trace2_data_intmax ("gh-client" , the_repository ,
323
- "get/immediate" , gh_client__includes_immediate );
324
-
325
- trace2_data_intmax ("gh-client" , the_repository ,
326
- "get/nr_objects" , gh_client__oidset_count );
327
-
328
- if (nr_loose )
329
- trace2_data_intmax ("gh-client" , the_repository ,
330
- "get/nr_loose" , nr_loose );
331
-
332
- if (nr_packfile )
333
- trace2_data_intmax ("gh-client" , the_repository ,
334
- "get/nr_packfile" , nr_packfile );
335
-
336
- if (err )
337
- trace2_data_intmax ("gh-client" , the_repository ,
338
- "get/error" , err );
339
-
340
- trace2_region_leave ("gh-client" , "get" , the_repository );
341
-
342
- oidset_clear (& gh_client__oidset_queued );
343
- gh_client__oidset_count = 0 ;
344
- gh_client__includes_immediate = 0 ;
345
-
346
- return err ;
350
+ return entry ;
347
351
}
348
352
349
353
void gh_client__queue_oid (const struct object_id * oid )
@@ -370,27 +374,97 @@ void gh_client__queue_oid_array(const struct object_id *oids, int oid_nr)
370
374
gh_client__queue_oid (& oids [k ]);
371
375
}
372
376
377
+ /*
378
+ * Bulk fetch all of the queued OIDs in the OIDSET.
379
+ */
373
380
int gh_client__drain_queue (enum gh_client__created * p_ghc )
374
381
{
382
+ struct gh_server__process * entry ;
383
+ struct child_process * process ;
384
+ int nr_loose = 0 ;
385
+ int nr_packfile = 0 ;
386
+ int err = 0 ;
387
+
375
388
* p_ghc = GHC__CREATED__NOTHING ;
376
389
377
390
if (!gh_client__oidset_count )
378
391
return 0 ;
379
392
380
- return gh_client__get (p_ghc );
393
+ entry = gh_client__find_long_running_process (CAP_OBJECTS );
394
+ if (!entry )
395
+ return -1 ;
396
+
397
+ trace2_region_enter ("gh-client" , "objects/post" , the_repository );
398
+
399
+ process = & entry -> subprocess .process ;
400
+
401
+ sigchain_push (SIGPIPE , SIG_IGN );
402
+
403
+ err = gh_client__send__objects_post (process );
404
+ if (!err )
405
+ err = gh_client__objects__receive_response (
406
+ process , p_ghc , & nr_loose , & nr_packfile );
407
+
408
+ sigchain_pop (SIGPIPE );
409
+
410
+ if (err ) {
411
+ subprocess_stop (& gh_server__subprocess_map ,
412
+ (struct subprocess_entry * )entry );
413
+ FREE_AND_NULL (entry );
414
+ }
415
+
416
+ trace2_data_intmax ("gh-client" , the_repository ,
417
+ "objects/post/nr_objects" , gh_client__oidset_count );
418
+ trace2_region_leave ("gh-client" , "objects/post" , the_repository );
419
+
420
+ oidset_clear (& gh_client__oidset_queued );
421
+ gh_client__oidset_count = 0 ;
422
+
423
+ return err ;
381
424
}
425
+
426
+ /*
427
+ * Get exactly 1 object immediately.
428
+ * Ignore any queued objects.
429
+ */
382
430
int gh_client__get_immediate (const struct object_id * oid ,
383
431
enum gh_client__created * p_ghc )
384
432
{
385
- gh_client__includes_immediate = 1 ;
433
+ struct gh_server__process * entry ;
434
+ struct child_process * process ;
435
+ int nr_loose = 0 ;
436
+ int nr_packfile = 0 ;
437
+ int err = 0 ;
386
438
387
439
// TODO consider removing this trace2. it is useful for interactive
388
440
// TODO debugging, but may generate way too much noise for a data
389
441
// TODO event.
390
442
trace2_printf ("gh_client__get_immediate: %s" , oid_to_hex (oid ));
391
443
392
- if (!oidset_insert (& gh_client__oidset_queued , oid ))
393
- gh_client__oidset_count ++ ;
444
+ entry = gh_client__find_long_running_process (CAP_OBJECTS );
445
+ if (!entry )
446
+ return -1 ;
447
+
448
+ trace2_region_enter ("gh-client" , "objects/get" , the_repository );
394
449
395
- return gh_client__drain_queue (p_ghc );
450
+ process = & entry -> subprocess .process ;
451
+
452
+ sigchain_push (SIGPIPE , SIG_IGN );
453
+
454
+ err = gh_client__send__objects_get (process , oid );
455
+ if (!err )
456
+ err = gh_client__objects__receive_response (
457
+ process , p_ghc , & nr_loose , & nr_packfile );
458
+
459
+ sigchain_pop (SIGPIPE );
460
+
461
+ if (err ) {
462
+ subprocess_stop (& gh_server__subprocess_map ,
463
+ (struct subprocess_entry * )entry );
464
+ FREE_AND_NULL (entry );
465
+ }
466
+
467
+ trace2_region_leave ("gh-client" , "objects/get" , the_repository );
468
+
469
+ return err ;
396
470
}
0 commit comments