channel_stack.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H
20 #define GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H
21 
23 // IMPORTANT NOTE:
24 //
25 // When you update this API, please make the corresponding changes to
26 // the C++ API in src/cpp/common/channel_filter.{h,cc}
28 
29 /* A channel filter defines how operations on a channel are implemented.
30  Channel filters are chained together to create full channels, and if those
31  chains are linear, then channel stacks provide a mechanism to minimize
32  allocations for that chain.
33  Call stacks are created by channel stacks and represent the per-call data
34  for that stack.
35 
36  Implementations should take care of the following details for a batch -
37  1. Synchronization is achieved with a CallCombiner. View
38  src/core/lib/iomgr/call_combiner.h for more details.
39  2. If the filter wants to inject an error on the way down, it needs to call
40  grpc_transport_stream_op_batch_finish_with_failure from within the call
41  combiner. This will cause any batch callbacks to be called with that error.
42  3. If the filter wants to inject an error on the way up (from a callback), it
43  should also inject that error in the recv_trailing_metadata callback so that
44  it can have an effect on the call status.
45 */
46 
48 
49 #include <stddef.h>
50 
51 #include <functional>
52 
55 #include <grpc/slice.h>
56 #include <grpc/status.h>
57 #include <grpc/support/log.h>
58 
73 
77  int is_first;
78  int is_last;
79 };
82  const void* server_transport_data;
84  const grpc_slice& path;
85  gpr_cycle_counter start_time; // Note: not populated in subchannel stack.
89 };
92  gpr_timespec latency; /* From call creating to enqueing of received status */
93 };
98  const char* error_string = nullptr;
99 };
100 
101 /* Channel filters specify:
102  1. the amount of memory needed in the channel & call (via the sizeof_XXX
103  members)
104  2. functions to initialize and destroy channel & call data
105  (init_XXX, destroy_XXX)
106  3. functions to implement call operations and channel operations (call_op,
107  channel_op)
108  4. a name, which is useful when debugging
109 
110  Members are laid out in approximate frequency of use order. */
112  /* Called to eg. send/receive data on a call.
113  See grpc_call_next_op on how to call the next element in the stack */
116  /* Create a promise to execute one call.
117  If this is non-null, it may be used in preference to
118  start_transport_stream_op_batch.
119  If this is used in preference to start_transport_stream_op_batch, the
120  following can be omitted also:
121  - calling init_call_elem, destroy_call_elem, set_pollset_or_pollset_set
122  - allocation of memory for call data
123  There is an on-going migration to move all filters to providing this, and
124  then to drop start_transport_stream_op_batch. */
127  grpc_core::NextPromiseFactory next_promise_factory);
128  /* Called to handle channel level operations - e.g. new calls, or transport
129  closure.
130  See grpc_channel_next_op on how to call the next element in the stack */
132 
133  /* sizeof(per call data) */
135  /* Initialize per call data.
136  elem is initialized at the start of the call, and elem->call_data is what
137  needs initializing.
138  The filter does not need to do any chaining.
139  server_transport_data is an opaque pointer. If it is NULL, this call is
140  on a client; if it is non-NULL, then it points to memory owned by the
141  transport and is on the server. Most filters want to ignore this
142  argument.
143  Implementations may assume that elem->call_data is all zeros. */
147  grpc_polling_entity* pollent);
148  /* Destroy per call data.
149  The filter does not need to do any chaining.
150  The bottom filter of a stack will be passed a non-NULL pointer to
151  \a then_schedule_closure that should be passed to GRPC_CLOSURE_SCHED when
152  destruction is complete. \a final_info contains data about the completed
153  call, mainly for reporting purposes. */
155  const grpc_call_final_info* final_info,
156  grpc_closure* then_schedule_closure);
157 
158  /* sizeof(per channel data) */
160  /* Initialize per-channel data.
161  elem is initialized at the creating of the channel, and elem->channel_data
162  is what needs initializing.
163  is_first, is_last designate this elements position in the stack, and are
164  useful for asserting correct configuration by upper layer code.
165  The filter does not need to do any chaining.
166  Implementations may assume that elem->channel_data is all zeros. */
169  /* Post init per-channel data.
170  Called after all channel elements have been successfully created. */
173  /* Destroy per channel data.
174  The filter does not need to do any chaining */
176 
177  /* Implement grpc_channel_get_info() */
179  const grpc_channel_info* channel_info);
180 
181  /* The name of this filter */
182  const char* name;
183 };
184 /* A channel_element tracks its filter and the filter requested memory within
185  a channel allocation */
189 };
190 
191 /* A call_element tracks its filter, the filter requested memory within
192  a channel allocation, and the filter requested memory within a call
193  allocation */
197  void* call_data;
198 };
199 
200 /* A channel stack tracks a set of related filters for one channel, and
201  guarantees they live within a single malloc() allocation */
204  bool is_client;
205  size_t count;
206  /* Memory required for a call stack (computed at channel stack
207  initialization) */
209  // TODO(ctiller): remove this mechanism... it's a hack to allow
210  // Channel to be separated from grpc_channel_stack's allocation. As the
211  // promise conversion continues, we'll reconsider what grpc_channel_stack
212  // should look like and this can go.
214 
215  // Minimal infrastructure to act like a RefCounted thing without converting
216  // everything.
217  // It's likely that we'll want to replace grpc_channel_stack with something
218  // less regimented once the promise conversion completes, so avoiding doing a
219  // full C++-ification for now.
220  void IncrementRefCount();
221  void Unref();
225  }
226 
228  grpc_core::CallArgs call_args);
229 };
230 
231 /* A call stack tracks a set of related filters for one call, and guarantees
232  they live within a single malloc() allocation */
234  /* shared refcount for this channel stack.
235  MUST be the first element: the underlying code calls destroy
236  with the address of the refcount, but higher layers prefer to think
237  about the address of the call stack itself. */
239  size_t count;
240 
241  // Minimal infrastructure to act like a RefCounted thing without converting
242  // everything.
243  // grpc_call_stack will be eliminated once the promise conversion completes.
244  void IncrementRefCount();
245  void Unref();
249  }
250 };
251 
252 /* Get a channel element given a channel stack and its index */
254  size_t i);
255 /* Get the last channel element in a channel stack */
258 
259 // A utility function for a filter to determine how many other instances
260 // of the same filter exist above it in the same stack. Intended to be
261 // used in the filter's init_channel_elem() method.
263  grpc_channel_stack* channel_stack, grpc_channel_element* elem);
264 
265 /* Get a call stack element given a call stack and an index */
267 
268 /* Determine memory required for a channel stack containing a set of filters */
269 size_t grpc_channel_stack_size(const grpc_channel_filter** filters,
270  size_t filter_count);
271 /* Initialize a channel stack given some filters */
273  int initial_refs, grpc_iomgr_cb_func destroy, void* destroy_arg,
274  const grpc_channel_filter** filters, size_t filter_count,
275  const grpc_channel_args* args, const char* name, grpc_channel_stack* stack);
276 /* Destroy a channel stack */
278 
279 /* Initialize a call stack given a channel stack. transport_server_data is
280  expected to be NULL on a client, or an opaque transport owned pointer on the
281  server. */
283  int initial_refs,
285  void* destroy_arg,
286  const grpc_call_element_args* elem_args);
287 /* Set a pollset or a pollset_set for a call stack: must occur before the first
288  * op is started */
290  grpc_polling_entity* pollent);
291 
292 #ifndef NDEBUG
293 #define GRPC_CALL_STACK_REF(call_stack, reason) \
294  grpc_stream_ref(&(call_stack)->refcount, reason)
295 #define GRPC_CALL_STACK_UNREF(call_stack, reason) \
296  grpc_stream_unref(&(call_stack)->refcount, reason)
297 #define GRPC_CHANNEL_STACK_REF(channel_stack, reason) \
298  grpc_stream_ref(&(channel_stack)->refcount, reason)
299 #define GRPC_CHANNEL_STACK_UNREF(channel_stack, reason) \
300  grpc_stream_unref(&(channel_stack)->refcount, reason)
301 #else
302 #define GRPC_CALL_STACK_REF(call_stack, reason) \
303  do { \
304  grpc_stream_ref(&(call_stack)->refcount); \
305  (void)(reason); \
306  } while (0);
307 #define GRPC_CALL_STACK_UNREF(call_stack, reason) \
308  do { \
309  grpc_stream_unref(&(call_stack)->refcount); \
310  (void)(reason); \
311  } while (0);
312 #define GRPC_CHANNEL_STACK_REF(channel_stack, reason) \
313  do { \
314  grpc_stream_ref(&(channel_stack)->refcount); \
315  (void)(reason); \
316  } while (0);
317 #define GRPC_CHANNEL_STACK_UNREF(channel_stack, reason) \
318  do { \
319  grpc_stream_unref(&(channel_stack)->refcount); \
320  (void)(reason); \
321  } while (0);
322 #endif
323 
325  GRPC_CHANNEL_STACK_REF(this, "smart_pointer");
326 }
327 
329  GRPC_CHANNEL_STACK_UNREF(this, "smart_pointer");
330 }
331 
333  GRPC_CALL_STACK_REF(this, "smart_pointer");
334 }
335 
336 inline void grpc_call_stack::Unref() {
337  GRPC_CALL_STACK_UNREF(this, "smart_pointer");
338 }
339 
340 /* Destroy a call stack */
342  const grpc_call_final_info* final_info,
343  grpc_closure* then_schedule_closure);
344 
345 /* Ignore set pollset{_set} - used by filters if they don't care about pollsets
346  * at all. Does nothing. */
349 /* Call the next operation in a call stack */
352 /* Call the next operation (depending on call directionality) in a channel
353  stack */
355 /* Pass through a request to get_channel_info() to the next child element */
357  const grpc_channel_info* channel_info);
358 
359 /* Given the top element of a channel stack, get the channel stack itself */
362 /* Given the top element of a call stack, get the call stack itself */
364 
365 void grpc_call_log_op(const char* file, int line, gpr_log_severity severity,
368 
371 
373 
374 #define GRPC_CALL_LOG_OP(sev, elem, op) \
375  do { \
376  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_channel)) { \
377  grpc_call_log_op(sev, elem, op); \
378  } \
379  } while (0)
380 
381 #endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H */
grpc_call_stack::count
size_t count
Definition: channel_stack.h:239
trace.h
grpc_channel_filter::get_channel_info
void(* get_channel_info)(grpc_channel_element *elem, const grpc_channel_info *channel_info)
Definition: channel_stack.h:178
grpc_core::CallCombiner
Definition: call_combiner.h:50
grpc_channel_stack_element
grpc_channel_element * grpc_channel_stack_element(grpc_channel_stack *stack, size_t i)
Definition: channel_stack.cc:78
log.h
grpc_channel_filter::name
const char * name
Definition: channel_stack.h:182
bloat_diff.severity
def severity
Definition: bloat_diff.py:143
grpc_channel_stack
Definition: channel_stack.h:202
channel_fwd.h
grpc_channel_filter::post_init_channel_elem
void(* post_init_channel_elem)(grpc_channel_stack *stk, grpc_channel_element *elem)
Definition: channel_stack.h:171
grpc_call_next_op
void grpc_call_next_op(grpc_call_element *elem, grpc_transport_stream_op_batch *op)
Definition: channel_stack.cc:251
polling_entity.h
slice.h
grpc_channel_stack::is_client
bool is_client
Definition: channel_stack.h:204
grpc_call_stack::IncrementRefCount
void IncrementRefCount()
Definition: channel_stack.h:332
grpc_channel_stack_from_top_element
grpc_channel_stack * grpc_channel_stack_from_top_element(grpc_channel_element *elem)
Definition: channel_stack.cc:269
grpc_core::Timestamp
Definition: src/core/lib/gprpp/time.h:62
GRPC_CHANNEL_STACK_UNREF
#define GRPC_CHANNEL_STACK_UNREF(channel_stack, reason)
Definition: channel_stack.h:299
grpc_channel_element
Definition: channel_stack.h:186
elem
Timer elem
Definition: event_engine/iomgr_event_engine/timer_heap_test.cc:109
grpc_status_code
grpc_status_code
Definition: include/grpc/impl/codegen/status.h:28
grpc_call_stats::latency
gpr_timespec latency
Definition: channel_stack.h:92
grpc_call_final_info::stats
grpc_call_stats stats
Definition: channel_stack.h:96
arena.h
file
Definition: bloaty/third_party/zlib/examples/gzappend.c:170
closure.h
setup.name
name
Definition: setup.py:542
grpc_call_final_info::final_status
grpc_status_code final_status
Definition: channel_stack.h:97
grpc_call_element::call_data
void * call_data
Definition: channel_stack.h:197
grpc_call_stack::Ref
grpc_core::RefCountedPtr< grpc_call_stack > Ref()
Definition: channel_stack.h:246
grpc_call_element
Definition: channel_stack.h:194
grpc_call_stack_destroy
void grpc_call_stack_destroy(grpc_call_stack *stack, const grpc_call_final_info *final_info, grpc_closure *then_schedule_closure)
Definition: channel_stack.cc:236
grpc_call_element_args::deadline
grpc_core::Timestamp deadline
Definition: channel_stack.h:86
grpc_call_stats
Definition: channel_stack.h:90
grpc_core::Arena
Definition: src/core/lib/resource_quota/arena.h:45
grpc_channel_args
Definition: grpc_types.h:132
grpc_channel_stack_no_post_init
void grpc_channel_stack_no_post_init(grpc_channel_stack *stk, grpc_channel_element *elem)
Definition: channel_stack.cc:282
grpc_transport_op
Definition: transport.h:452
grpc_call_element_args::call_combiner
grpc_core::CallCombiner * call_combiner
Definition: channel_stack.h:88
grpc_call_stack::Unref
void Unref()
Definition: channel_stack.h:336
gpr_log_severity
gpr_log_severity
Definition: include/grpc/impl/codegen/log.h:45
grpc_channel_element_args::is_last
int is_last
Definition: channel_stack.h:78
status.h
grpc_types.h
grpc_channel_stack::call_stack_size
size_t call_stack_size
Definition: channel_stack.h:208
grpc_channel_filter::make_call_promise
grpc_core::ArenaPromise< grpc_core::ServerMetadataHandle >(* make_call_promise)(grpc_channel_element *elem, grpc_core::CallArgs call_args, grpc_core::NextPromiseFactory next_promise_factory)
Definition: channel_stack.h:125
grpc_channel_filter::init_channel_elem
grpc_error_handle(* init_channel_elem)(grpc_channel_element *elem, grpc_channel_element_args *args)
Definition: channel_stack.h:167
context.h
GRPC_CALL_STACK_UNREF
#define GRPC_CALL_STACK_UNREF(call_stack, reason)
Definition: channel_stack.h:295
grpc_call_element_args::arena
grpc_core::Arena * arena
Definition: channel_stack.h:87
grpc_call_stack_init
grpc_error_handle grpc_call_stack_init(grpc_channel_stack *channel_stack, int initial_refs, grpc_iomgr_cb_func destroy, void *destroy_arg, const grpc_call_element_args *elem_args)
Definition: channel_stack.cc:180
asyncio_get_stats.args
args
Definition: asyncio_get_stats.py:40
grpc_core::NextPromiseFactory
std::function< ArenaPromise< ServerMetadataHandle >(CallArgs)> NextPromiseFactory
Definition: transport.h:165
GRPC_STATUS_OK
@ GRPC_STATUS_OK
Definition: include/grpc/impl/codegen/status.h:30
grpc_channel_filter::sizeof_channel_data
size_t sizeof_channel_data
Definition: channel_stack.h:159
grpc_core::RefCountedPtr< grpc_channel_stack >
grpc_call_stats::transport_stream_stats
grpc_transport_stream_stats transport_stream_stats
Definition: channel_stack.h:91
grpc_channel_filter::destroy_channel_elem
void(* destroy_channel_elem)(grpc_channel_element *elem)
Definition: channel_stack.h:175
grpc_channel_element::channel_data
void * channel_data
Definition: channel_stack.h:188
grpc_channel_element::filter
const grpc_channel_filter * filter
Definition: channel_stack.h:187
grpc_call_stack
Definition: channel_stack.h:233
grpc_call_element::filter
const grpc_channel_filter * filter
Definition: channel_stack.h:195
grpc_call_stack_ignore_set_pollset_or_pollset_set
void grpc_call_stack_ignore_set_pollset_or_pollset_set(grpc_call_element *elem, grpc_polling_entity *pollent)
Definition: channel_stack.cc:233
grpc_channel_element_args::channel_stack
grpc_channel_stack * channel_stack
Definition: channel_stack.h:75
grpc_call_stack::refcount
grpc_stream_refcount refcount
Definition: channel_stack.h:238
grpc_error_handle
grpc_error * grpc_error_handle
Definition: error.h:50
call_combiner.h
grpc_call_element_args::path
const grpc_slice & path
Definition: channel_stack.h:84
grpc_trace_channel
grpc_core::TraceFlag grpc_trace_channel
arena_promise.h
time.h
grpc_slice
Definition: include/grpc/impl/codegen/slice.h:65
grpc_channel_stack::Unref
void Unref()
Definition: channel_stack.h:328
grpc_core::CallArgs
Definition: transport.h:159
stack
NodeStack stack
Definition: cord_rep_btree.cc:356
error.h
grpc_channel_stack_last_element
grpc_channel_element * grpc_channel_stack_last_element(grpc_channel_stack *stack)
Definition: channel_stack.cc:83
grpc_channel_stack::Ref
grpc_core::RefCountedPtr< grpc_channel_stack > Ref()
Definition: channel_stack.h:222
grpc_polling_entity
Definition: polling_entity.h:38
grpc_call_element_args
Definition: channel_stack.h:80
grpc_channel_filter::sizeof_call_data
size_t sizeof_call_data
Definition: channel_stack.h:134
grpc_channel_stack_destroy
void grpc_channel_stack_destroy(grpc_channel_stack *stack)
Definition: channel_stack.cc:166
grpc_channel_filter::destroy_call_elem
void(* destroy_call_elem)(grpc_call_element *elem, const grpc_call_final_info *final_info, grpc_closure *then_schedule_closure)
Definition: channel_stack.h:154
manual_constructor.h
grpc_call_stack_element
grpc_call_element * grpc_call_stack_element(grpc_call_stack *stack, size_t i)
Definition: channel_stack.cc:100
grpc_channel_filter
Definition: channel_stack.h:111
gpr_types.h
grpc_call_final_info::error_string
const char * error_string
Definition: channel_stack.h:98
grpc_core::TraceFlag
Definition: debug/trace.h:63
grpc_channel_filter::start_transport_stream_op_batch
void(* start_transport_stream_op_batch)(grpc_call_element *elem, grpc_transport_stream_op_batch *op)
Definition: channel_stack.h:114
GRPC_CHANNEL_STACK_REF
#define GRPC_CHANNEL_STACK_REF(channel_stack, reason)
Definition: channel_stack.h:297
grpc_channel_element_args::channel_args
const grpc_channel_args * channel_args
Definition: channel_stack.h:76
grpc_core::ArenaPromise
Definition: arena_promise.h:152
grpc_channel_stack::count
size_t count
Definition: channel_stack.h:205
grpc_transport_stream_stats
Definition: transport.h:249
grpc_channel_stack::MakeCallPromise
grpc_core::ArenaPromise< grpc_core::ServerMetadataHandle > MakeCallPromise(grpc_core::CallArgs call_args)
Definition: channel_stack.cc:304
grpc_channel_stack::IncrementRefCount
void IncrementRefCount()
Definition: channel_stack.h:324
grpc_call_stack_from_top_element
grpc_call_stack * grpc_call_stack_from_top_element(grpc_call_element *elem)
Definition: channel_stack.cc:276
grpc_iomgr_cb_func
void(* grpc_iomgr_cb_func)(void *arg, grpc_error_handle error)
Definition: closure.h:53
grpc_channel_stack_size
size_t grpc_channel_stack_size(const grpc_channel_filter **filters, size_t filter_count)
Definition: channel_stack.cc:51
grpc_call_context_element
Definition: core/lib/channel/context.h:51
grpc_call_log_op
void grpc_call_log_op(const char *file, int line, gpr_log_severity severity, grpc_call_element *elem, grpc_transport_stream_op_batch *op)
Definition: transport_op_string.cc:141
grpc_channel_stack_filter_instance_number
size_t grpc_channel_stack_filter_instance_number(grpc_channel_stack *channel_stack, grpc_channel_element *elem)
Definition: channel_stack.cc:88
regen-readme.line
line
Definition: regen-readme.py:30
grpc_call_element_args::context
grpc_call_context_element * context
Definition: channel_stack.h:83
grpc_call_element_args::call_stack
grpc_call_stack * call_stack
Definition: channel_stack.h:81
grpc_call_stack_set_pollset_or_pollset_set
void grpc_call_stack_set_pollset_or_pollset_set(grpc_call_stack *call_stack, grpc_polling_entity *pollent)
Definition: channel_stack.cc:219
ref_counted_ptr.h
transport.h
grpc_channel_stack::on_destroy
grpc_core::ManualConstructor< std::function< void()> > on_destroy
Definition: channel_stack.h:213
grpc_channel_element_args
Definition: channel_stack.h:74
grpc_channel_next_get_info
void grpc_channel_next_get_info(grpc_channel_element *elem, const grpc_channel_info *channel_info)
Definition: channel_stack.cc:258
grpc_call_final_info
Definition: channel_stack.h:95
gpr_timespec
Definition: gpr_types.h:50
grpc_channel_filter::set_pollset_or_pollset_set
void(* set_pollset_or_pollset_set)(grpc_call_element *elem, grpc_polling_entity *pollent)
Definition: channel_stack.h:146
grpc_error
Definition: error_internal.h:42
grpc_channel_filter::start_transport_op
void(* start_transport_op)(grpc_channel_element *elem, grpc_transport_op *op)
Definition: channel_stack.h:131
function
std::function< bool(GrpcTool *, int, const char **, const CliCredentials &, GrpcToolOutputCallback)> function
Definition: grpc_tool.cc:250
GRPC_CALL_STACK_REF
#define GRPC_CALL_STACK_REF(call_stack, reason)
Definition: channel_stack.h:293
grpc_call_element_args::server_transport_data
const void * server_transport_data
Definition: channel_stack.h:82
grpc_call_element_args::start_time
gpr_cycle_counter start_time
Definition: channel_stack.h:85
grpc_channel_info
Definition: grpc_types.h:720
grpc_transport_stream_op_batch
Definition: transport.h:284
grpc_closure
Definition: closure.h:56
op
static grpc_op * op
Definition: test/core/fling/client.cc:47
grpc_channel_stack::refcount
grpc_stream_refcount refcount
Definition: channel_stack.h:203
grpc_call_element::channel_data
void * channel_data
Definition: channel_stack.h:196
grpc_channel_stack_init
grpc_error_handle grpc_channel_stack_init(int initial_refs, grpc_iomgr_cb_func destroy, void *destroy_arg, const grpc_channel_filter **filters, size_t filter_count, const grpc_channel_args *args, const char *name, grpc_channel_stack *stack)
Definition: channel_stack.cc:105
grpc_channel_next_op
void grpc_channel_next_op(grpc_channel_element *elem, grpc_transport_op *op)
Definition: channel_stack.cc:264
destroy
static std::function< void(void *, Slot *)> destroy
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:42
grpc_stream_refcount
Definition: transport.h:178
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
grpc_channel_element_args::is_first
int is_first
Definition: channel_stack.h:77
grpc_channel_filter::init_call_elem
grpc_error_handle(* init_call_elem)(grpc_call_element *elem, const grpc_call_element_args *args)
Definition: channel_stack.h:144
grpc_core::ManualConstructor
Definition: manual_constructor.h:103
time_precise.h
port_platform.h


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:58:44