pub struct Mutex<T>where
T: ?Sized,{ /* private fields */ }Expand description
A mutual exclusion primitive useful for protecting shared data
This mutex will block threads waiting for the lock to become available. The
mutex can be created via a new constructor. Each mutex has a type parameter
which represents the data that it is protecting. The data can only be accessed
through the RAII guards returned from lock and try_lock, which
guarantees that the data is only ever accessed when the mutex is locked.
§Poisoning
The mutexes in this module implement a strategy called “poisoning” where a mutex becomes poisoned if it recognizes that the thread holding it has panicked.
Once a mutex is poisoned, all other threads are unable to access the data by
default as it is likely tainted (some invariant is not being upheld). For a
mutex, this means that the lock and try_lock methods return a
Result which indicates whether a mutex has been poisoned or not. Most
usage of a mutex will simply unwrap() these results, propagating panics
among threads to ensure that a possibly invalid invariant is not witnessed.
Poisoning is only advisory: the PoisonError type has an into_inner
method which will return the guard that would have otherwise been returned
on a successful lock. This allows access to the data, despite the lock being
poisoned.
In addition, the panic detection is not ideal, so even unpoisoned mutexes need to be handled with care, since certain panics may have been skipped. Here is a non-exhaustive list of situations where this might occur:
-
If a mutex is locked while a panic is underway, e.g. within a
Dropimplementation or a panic hook, panicking for the second time while the lock is held will leave the mutex unpoisoned. Note that while double panic usually aborts the program,catch_unwindcan prevent this. -
Locking and unlocking the mutex across different panic contexts, e.g. by storing the guard to a
CellwithinDrop::dropand accessing it outside, or vice versa, can affect poisoning status in an unexpected way. -
Foreign exceptions do not currently trigger poisoning even in absence of other panics.
While this rarely happens in realistic code, unsafe code cannot rely on
poisoning for soundness, since the behavior of poisoning can depend on
outside context. Here’s an example of incorrect use of poisoning:
use std::sync::Mutex;
struct MutexBox<T> {
data: Mutex<*mut T>,
}
impl<T> MutexBox<T> {
pub fn new(value: T) -> Self {
Self {
data: Mutex::new(Box::into_raw(Box::new(value))),
}
}
pub fn replace_with(&self, f: impl FnOnce(T) -> T) {
let ptr = self.data.lock().expect("poisoned");
// While `f` is running, the data is moved out of `*ptr`. If `f`
// panics, `*ptr` keeps pointing at a dropped value. The intention
// is that this will poison the mutex, so the following calls to
// `replace_with` will panic without reading `*ptr`. But since
// poisoning is not guaranteed to occur if this is run from a panic
// hook, this can lead to use-after-free.
unsafe {
(*ptr).write(f((*ptr).read()));
}
}
}§Examples
use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc::channel;
const N: usize = 10;
// Spawn a few threads to increment a shared variable (non-atomically), and
// let the main thread know once all increments are done.
//
// Here we're using an Arc to share memory among threads, and the data inside
// the Arc is protected with a mutex.
let data = Arc::new(Mutex::new(0));
let (tx, rx) = channel();
for _ in 0..N {
let (data, tx) = (Arc::clone(&data), tx.clone());
thread::spawn(move || {
// The shared state can only be accessed once the lock is held.
// Our non-atomic increment is safe because we're the only thread
// which can access the shared state when the lock is held.
//
// We unwrap() the return value to assert that we are not expecting
// threads to ever fail while holding the lock.
let mut data = data.lock().unwrap();
*data += 1;
if *data == N {
tx.send(()).unwrap();
}
// the lock is unlocked here when `data` goes out of scope.
});
}
rx.recv().unwrap();To recover from a poisoned mutex:
use std::sync::{Arc, Mutex};
use std::thread;
let lock = Arc::new(Mutex::new(0_u32));
let lock2 = Arc::clone(&lock);
let _ = thread::spawn(move || -> () {
// This thread will acquire the mutex first, unwrapping the result of
// `lock` because the lock has not been poisoned.
let _guard = lock2.lock().unwrap();
// This panic while holding the lock (`_guard` is in scope) will poison
// the mutex.
panic!();
}).join();
// The lock is poisoned by this point, but the returned result can be
// pattern matched on to return the underlying guard on both branches.
let mut guard = match lock.lock() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
};
*guard += 1;To unlock a mutex guard sooner than the end of the enclosing scope, either create an inner scope or drop the guard manually.
use std::sync::{Arc, Mutex};
use std::thread;
const N: usize = 3;
let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));
let res_mutex = Arc::new(Mutex::new(0));
let mut threads = Vec::with_capacity(N);
(0..N).for_each(|_| {
let data_mutex_clone = Arc::clone(&data_mutex);
let res_mutex_clone = Arc::clone(&res_mutex);
threads.push(thread::spawn(move || {
// Here we use a block to limit the lifetime of the lock guard.
let result = {
let mut data = data_mutex_clone.lock().unwrap();
// This is the result of some important and long-ish work.
let result = data.iter().fold(0, |acc, x| acc + x * 2);
data.push(result);
result
// The mutex guard gets dropped here, together with any other values
// created in the critical section.
};
// The guard created here is a temporary dropped at the end of the statement, i.e.
// the lock would not remain being held even if the thread did some additional work.
*res_mutex_clone.lock().unwrap() += result;
}));
});
let mut data = data_mutex.lock().unwrap();
// This is the result of some important and long-ish work.
let result = data.iter().fold(0, |acc, x| acc + x * 2);
data.push(result);
// We drop the `data` explicitly because it's not necessary anymore and the
// thread still has work to do. This allows other threads to start working on
// the data immediately, without waiting for the rest of the unrelated work
// to be done here.
//
// It's even more important here than in the threads because we `.join` the
// threads after that. If we had not dropped the mutex guard, a thread could
// be waiting forever for it, causing a deadlock.
// As in the threads, a block could have been used instead of calling the
// `drop` function.
drop(data);
// Here the mutex guard is not assigned to a variable and so, even if the
// scope does not end after this line, the mutex is still released: there is
// no deadlock.
*res_mutex.lock().unwrap() += result;
threads.into_iter().for_each(|thread| {
thread
.join()
.expect("The thread creating or execution failed !")
});
assert_eq!(*res_mutex.lock().unwrap(), 800);Implementations§
Source§impl<T> Mutex<T>
impl<T> Mutex<T>
1.0.0 (const: 1.63.0) · Sourcepub const fn new(t: T) -> Mutex<T>
pub const fn new(t: T) -> Mutex<T>
Creates a new mutex in an unlocked state ready for use.
§Examples
use std::sync::Mutex;
let mutex = Mutex::new(0);Sourcepub fn get_cloned(&self) -> Result<T, PoisonError<()>>where
T: Clone,
🔬This is a nightly-only experimental API. (lock_value_accessors)
pub fn get_cloned(&self) -> Result<T, PoisonError<()>>where
T: Clone,
lock_value_accessors)Returns the contained value by cloning it.
§Errors
If another user of this mutex panicked while holding the mutex, then this call will return an error instead.
§Examples
#![feature(lock_value_accessors)]
use std::sync::Mutex;
let mut mutex = Mutex::new(7);
assert_eq!(mutex.get_cloned().unwrap(), 7);Sourcepub fn set(&self, value: T) -> Result<(), PoisonError<T>>
🔬This is a nightly-only experimental API. (lock_value_accessors)
pub fn set(&self, value: T) -> Result<(), PoisonError<T>>
lock_value_accessors)Sets the contained value.
§Errors
If another user of this mutex panicked while holding the mutex, then
this call will return an error containing the provided value instead.
§Examples
#![feature(lock_value_accessors)]
use std::sync::Mutex;
let mut mutex = Mutex::new(7);
assert_eq!(mutex.get_cloned().unwrap(), 7);
mutex.set(11).unwrap();
assert_eq!(mutex.get_cloned().unwrap(), 11);Sourcepub fn replace(&self, value: T) -> Result<T, PoisonError<T>>
🔬This is a nightly-only experimental API. (lock_value_accessors)
pub fn replace(&self, value: T) -> Result<T, PoisonError<T>>
lock_value_accessors)Replaces the contained value with value, and returns the old contained value.
§Errors
If another user of this mutex panicked while holding the mutex, then
this call will return an error containing the provided value instead.
§Examples
#![feature(lock_value_accessors)]
use std::sync::Mutex;
let mut mutex = Mutex::new(7);
assert_eq!(mutex.replace(11).unwrap(), 7);
assert_eq!(mutex.get_cloned().unwrap(), 11);Source§impl<T> Mutex<T>where
T: ?Sized,
impl<T> Mutex<T>where
T: ?Sized,
1.0.0 · Sourcepub fn lock(&self) -> Result<MutexGuard<'_, T>, PoisonError<MutexGuard<'_, T>>>
pub fn lock(&self) -> Result<MutexGuard<'_, T>, PoisonError<MutexGuard<'_, T>>>
Acquires a mutex, blocking the current thread until it is able to do so.
This function will block the local thread until it is available to acquire the mutex. Upon returning, the thread is the only thread with the lock held. An RAII guard is returned to allow scoped unlock of the lock. When the guard goes out of scope, the mutex will be unlocked.
The exact behavior on locking a mutex in the thread which already holds the lock is left unspecified. However, this function will not return on the second call (it might panic or deadlock, for example).
§Errors
If another user of this mutex panicked while holding the mutex, then this call will return an error once the mutex is acquired. The acquired mutex guard will be contained in the returned error.
§Panics
This function might panic when called if the lock is already held by the current thread.
§Examples
use std::sync::{Arc, Mutex};
use std::thread;
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
thread::spawn(move || {
*c_mutex.lock().unwrap() = 10;
}).join().expect("thread::spawn failed");
assert_eq!(*mutex.lock().unwrap(), 10);Examples found in repository?
146fn init_saved_indirect_parameters(
147 render_device: Res<RenderDevice>,
148 gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
149 saved_indirect_parameters: Res<SavedIndirectParameters>,
150) {
151 let mut saved_indirect_parameters = saved_indirect_parameters.0.lock().unwrap();
152 *saved_indirect_parameters = Some(SavedIndirectParametersData {
153 data: vec![],
154 count: 0,
155 occlusion_culling_supported: gpu_preprocessing_support.is_culling_supported(),
156 // In order to determine how many meshes were culled, we look at the indirect count buffer
157 // that Bevy only populates if the platform supports `multi_draw_indirect_count`. So, if we
158 // don't have that feature, then we don't bother to display how many meshes were culled.
159 occlusion_culling_introspection_supported: render_device
160 .features()
161 .contains(WgpuFeatures::MULTI_DRAW_INDIRECT_COUNT),
162 });
163}
164
165/// The demo's current settings.
166#[derive(Resource)]
167struct AppStatus {
168 /// Whether occlusion culling is presently enabled.
169 ///
170 /// By default, this is set to true.
171 occlusion_culling: bool,
172}
173
174impl Default for AppStatus {
175 fn default() -> Self {
176 AppStatus {
177 occlusion_culling: true,
178 }
179 }
180}
181
182fn main() {
183 let render_debug_flags = RenderDebugFlags::ALLOW_COPIES_FROM_INDIRECT_PARAMETERS;
184
185 App::new()
186 .add_plugins(
187 DefaultPlugins
188 .set(WindowPlugin {
189 primary_window: Some(Window {
190 title: "Bevy Occlusion Culling Example".into(),
191 ..default()
192 }),
193 ..default()
194 })
195 .set(RenderPlugin {
196 debug_flags: render_debug_flags,
197 ..default()
198 })
199 .set(PbrPlugin {
200 debug_flags: render_debug_flags,
201 ..default()
202 }),
203 )
204 .add_plugins(ReadbackIndirectParametersPlugin)
205 .init_resource::<AppStatus>()
206 .add_systems(Startup, setup)
207 .add_systems(Update, spin_small_cubes)
208 .add_systems(Update, spin_large_cube)
209 .add_systems(Update, update_status_text)
210 .add_systems(Update, toggle_occlusion_culling_on_request)
211 .run();
212}
213
214impl Plugin for ReadbackIndirectParametersPlugin {
215 fn build(&self, app: &mut App) {
216 // Create the `SavedIndirectParameters` resource that we're going to use
217 // to communicate between the thread that the GPU-to-CPU readback
218 // callback runs on and the main application threads. This resource is
219 // atomically reference counted. We store one reference to the
220 // `SavedIndirectParameters` in the main app and another reference in
221 // the render app.
222 let saved_indirect_parameters = SavedIndirectParameters::new();
223 app.insert_resource(saved_indirect_parameters.clone());
224
225 // Fetch the render app.
226 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
227 return;
228 };
229
230 render_app
231 // Insert another reference to the `SavedIndirectParameters`.
232 .insert_resource(saved_indirect_parameters)
233 // Setup the parameters in RenderStartup.
234 .add_systems(RenderStartup, init_saved_indirect_parameters)
235 .init_resource::<IndirectParametersStagingBuffers>()
236 .add_systems(ExtractSchedule, readback_indirect_parameters)
237 .add_systems(
238 Render,
239 create_indirect_parameters_staging_buffers
240 .in_set(RenderSystems::PrepareResourcesFlush),
241 )
242 // Add the node that allows us to read the indirect parameters back
243 // from the GPU to the CPU, which allows us to determine how many
244 // meshes were culled.
245 .add_render_graph_node::<ReadbackIndirectParametersNode>(
246 Core3d,
247 ReadbackIndirectParameters,
248 )
249 // We read back the indirect parameters any time after
250 // `EndMainPass`. Readback doesn't particularly need to execute
251 // before `EndMainPassPostProcessing`, but we specify that anyway
252 // because we want to make the indirect parameters run before
253 // *something* in the graph, and `EndMainPassPostProcessing` is a
254 // good a node as any other.
255 .add_render_graph_edges(
256 Core3d,
257 (
258 Node3d::EndMainPass,
259 ReadbackIndirectParameters,
260 Node3d::EndMainPassPostProcessing,
261 ),
262 );
263 }
264}
265
266/// Spawns all the objects in the scene.
267fn setup(
268 mut commands: Commands,
269 asset_server: Res<AssetServer>,
270 mut meshes: ResMut<Assets<Mesh>>,
271 mut materials: ResMut<Assets<StandardMaterial>>,
272) {
273 spawn_small_cubes(&mut commands, &mut meshes, &mut materials);
274 spawn_large_cube(&mut commands, &asset_server, &mut meshes, &mut materials);
275 spawn_light(&mut commands);
276 spawn_camera(&mut commands);
277 spawn_help_text(&mut commands);
278}
279
280/// Spawns the rotating sphere of small cubes.
281fn spawn_small_cubes(
282 commands: &mut Commands,
283 meshes: &mut Assets<Mesh>,
284 materials: &mut Assets<StandardMaterial>,
285) {
286 // Add the cube mesh.
287 let small_cube = meshes.add(Cuboid::new(
288 SMALL_CUBE_SIZE,
289 SMALL_CUBE_SIZE,
290 SMALL_CUBE_SIZE,
291 ));
292
293 // Add the cube material.
294 let small_cube_material = materials.add(StandardMaterial {
295 base_color: SILVER.into(),
296 ..default()
297 });
298
299 // Create the entity that the small cubes will be parented to. This is the
300 // entity that we rotate.
301 let sphere_parent = commands
302 .spawn(Transform::from_translation(Vec3::ZERO))
303 .insert(Visibility::default())
304 .insert(SphereParent)
305 .id();
306
307 // Now we have to figure out where to place the cubes. To do that, we create
308 // a sphere mesh, but we don't add it to the scene. Instead, we inspect the
309 // sphere mesh to find the positions of its vertices, and spawn a small cube
310 // at each one. That way, we end up with a bunch of cubes arranged in a
311 // spherical shape.
312
313 // Create the sphere mesh, and extract the positions of its vertices.
314 let sphere = Sphere::new(OUTER_RADIUS)
315 .mesh()
316 .ico(OUTER_SUBDIVISION_COUNT)
317 .unwrap();
318 let sphere_positions = sphere.attribute(Mesh::ATTRIBUTE_POSITION).unwrap();
319
320 // At each vertex, create a small cube.
321 for sphere_position in sphere_positions.as_float3().unwrap() {
322 let sphere_position = Vec3::from_slice(sphere_position);
323 let small_cube = commands
324 .spawn(Mesh3d(small_cube.clone()))
325 .insert(MeshMaterial3d(small_cube_material.clone()))
326 .insert(Transform::from_translation(sphere_position))
327 .id();
328 commands.entity(sphere_parent).add_child(small_cube);
329 }
330}
331
332/// Spawns the large cube at the center of the screen.
333///
334/// This cube rotates chaotically and occludes small cubes behind it.
335fn spawn_large_cube(
336 commands: &mut Commands,
337 asset_server: &AssetServer,
338 meshes: &mut Assets<Mesh>,
339 materials: &mut Assets<StandardMaterial>,
340) {
341 commands
342 .spawn(Mesh3d(meshes.add(Cuboid::new(
343 LARGE_CUBE_SIZE,
344 LARGE_CUBE_SIZE,
345 LARGE_CUBE_SIZE,
346 ))))
347 .insert(MeshMaterial3d(materials.add(StandardMaterial {
348 base_color: WHITE.into(),
349 base_color_texture: Some(asset_server.load("branding/icon.png")),
350 ..default()
351 })))
352 .insert(Transform::IDENTITY)
353 .insert(LargeCube);
354}
355
356// Spins the outer sphere a bit every frame.
357//
358// This ensures that the set of cubes that are hidden and shown varies over
359// time.
360fn spin_small_cubes(mut sphere_parents: Query<&mut Transform, With<SphereParent>>) {
361 for mut sphere_parent_transform in &mut sphere_parents {
362 sphere_parent_transform.rotate_y(ROTATION_SPEED);
363 }
364}
365
366/// Spins the large cube a bit every frame.
367///
368/// The chaotic rotation adds a bit of randomness to the scene to better
369/// demonstrate the dynamicity of the occlusion culling.
370fn spin_large_cube(mut large_cubes: Query<&mut Transform, With<LargeCube>>) {
371 for mut transform in &mut large_cubes {
372 transform.rotate(Quat::from_euler(
373 EulerRot::XYZ,
374 0.13 * ROTATION_SPEED,
375 0.29 * ROTATION_SPEED,
376 0.35 * ROTATION_SPEED,
377 ));
378 }
379}
380
381/// Spawns a directional light to illuminate the scene.
382fn spawn_light(commands: &mut Commands) {
383 commands
384 .spawn(DirectionalLight::default())
385 .insert(Transform::from_rotation(Quat::from_euler(
386 EulerRot::ZYX,
387 0.0,
388 PI * -0.15,
389 PI * -0.15,
390 )));
391}
392
393/// Spawns a camera that includes the depth prepass and occlusion culling.
394fn spawn_camera(commands: &mut Commands) {
395 commands
396 .spawn(Camera3d::default())
397 .insert(Transform::from_xyz(0.0, 0.0, 9.0).looking_at(Vec3::ZERO, Vec3::Y))
398 .insert(DepthPrepass)
399 .insert(OcclusionCulling);
400}
401
402/// Spawns the help text at the upper left of the screen.
403fn spawn_help_text(commands: &mut Commands) {
404 commands.spawn((
405 Text::new(""),
406 Node {
407 position_type: PositionType::Absolute,
408 top: px(12),
409 left: px(12),
410 ..default()
411 },
412 ));
413}
414
415impl render_graph::Node for ReadbackIndirectParametersNode {
416 fn run<'w>(
417 &self,
418 _: &mut RenderGraphContext,
419 render_context: &mut RenderContext<'w>,
420 world: &'w World,
421 ) -> Result<(), NodeRunError> {
422 // Extract the buffers that hold the GPU indirect draw parameters from
423 // the world resources. We're going to read those buffers to determine
424 // how many meshes were actually drawn.
425 let (Some(indirect_parameters_buffers), Some(indirect_parameters_mapping_buffers)) = (
426 world.get_resource::<IndirectParametersBuffers>(),
427 world.get_resource::<IndirectParametersStagingBuffers>(),
428 ) else {
429 return Ok(());
430 };
431
432 // Get the indirect parameters buffers corresponding to the opaque 3D
433 // phase, since all our meshes are in that phase.
434 let Some(phase_indirect_parameters_buffers) =
435 indirect_parameters_buffers.get(&TypeId::of::<Opaque3d>())
436 else {
437 return Ok(());
438 };
439
440 // Grab both the buffers we're copying from and the staging buffers
441 // we're copying to. Remember that we can't map the indirect parameters
442 // buffers directly, so we have to copy their contents to a staging
443 // buffer.
444 let (
445 Some(indexed_data_buffer),
446 Some(indexed_batch_sets_buffer),
447 Some(indirect_parameters_staging_data_buffer),
448 Some(indirect_parameters_staging_batch_sets_buffer),
449 ) = (
450 phase_indirect_parameters_buffers.indexed.data_buffer(),
451 phase_indirect_parameters_buffers
452 .indexed
453 .batch_sets_buffer(),
454 indirect_parameters_mapping_buffers.data.as_ref(),
455 indirect_parameters_mapping_buffers.batch_sets.as_ref(),
456 )
457 else {
458 return Ok(());
459 };
460
461 // Copy from the indirect parameters buffers to the staging buffers.
462 render_context.command_encoder().copy_buffer_to_buffer(
463 indexed_data_buffer,
464 0,
465 indirect_parameters_staging_data_buffer,
466 0,
467 indexed_data_buffer.size(),
468 );
469 render_context.command_encoder().copy_buffer_to_buffer(
470 indexed_batch_sets_buffer,
471 0,
472 indirect_parameters_staging_batch_sets_buffer,
473 0,
474 indexed_batch_sets_buffer.size(),
475 );
476
477 Ok(())
478 }
479}
480
481/// Creates the staging buffers that we use to read back the indirect parameters
482/// from the GPU to the CPU.
483///
484/// We read the indirect parameters from the GPU to the CPU in order to display
485/// the number of meshes that were culled each frame.
486///
487/// We need these staging buffers because `wgpu` doesn't allow us to read the
488/// contents of the indirect parameters buffers directly. We must first copy
489/// them from the GPU to a staging buffer, and then read the staging buffer.
490fn create_indirect_parameters_staging_buffers(
491 mut indirect_parameters_staging_buffers: ResMut<IndirectParametersStagingBuffers>,
492 indirect_parameters_buffers: Res<IndirectParametersBuffers>,
493 render_device: Res<RenderDevice>,
494) {
495 let Some(phase_indirect_parameters_buffers) =
496 indirect_parameters_buffers.get(&TypeId::of::<Opaque3d>())
497 else {
498 return;
499 };
500
501 // Fetch the indirect parameters buffers that we're going to copy from.
502 let (Some(indexed_data_buffer), Some(indexed_batch_set_buffer)) = (
503 phase_indirect_parameters_buffers.indexed.data_buffer(),
504 phase_indirect_parameters_buffers
505 .indexed
506 .batch_sets_buffer(),
507 ) else {
508 return;
509 };
510
511 // Build the staging buffers. Make sure they have the same sizes as the
512 // buffers we're copying from.
513 indirect_parameters_staging_buffers.data =
514 Some(render_device.create_buffer(&BufferDescriptor {
515 label: Some("indexed data staging buffer"),
516 size: indexed_data_buffer.size(),
517 usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
518 mapped_at_creation: false,
519 }));
520 indirect_parameters_staging_buffers.batch_sets =
521 Some(render_device.create_buffer(&BufferDescriptor {
522 label: Some("indexed batch set staging buffer"),
523 size: indexed_batch_set_buffer.size(),
524 usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
525 mapped_at_creation: false,
526 }));
527}
528
529/// Updates the app status text at the top of the screen.
530fn update_status_text(
531 saved_indirect_parameters: Res<SavedIndirectParameters>,
532 mut texts: Query<&mut Text>,
533 meshes: Query<Entity, With<Mesh3d>>,
534 app_status: Res<AppStatus>,
535) {
536 // How many meshes are in the scene?
537 let total_mesh_count = meshes.iter().count();
538
539 // Sample the rendered object count. Note that we don't synchronize beyond
540 // locking the data and therefore this will value will generally at least
541 // one frame behind. This is fine; this app is just a demonstration after
542 // all.
543 let (
544 rendered_object_count,
545 occlusion_culling_supported,
546 occlusion_culling_introspection_supported,
547 ): (u32, bool, bool) = {
548 let saved_indirect_parameters = saved_indirect_parameters.lock().unwrap();
549 let Some(saved_indirect_parameters) = saved_indirect_parameters.as_ref() else {
550 // Bail out early if the resource isn't initialized yet.
551 return;
552 };
553 (
554 saved_indirect_parameters
555 .data
556 .iter()
557 .take(saved_indirect_parameters.count as usize)
558 .map(|indirect_parameters| indirect_parameters.instance_count)
559 .sum(),
560 saved_indirect_parameters.occlusion_culling_supported,
561 saved_indirect_parameters.occlusion_culling_introspection_supported,
562 )
563 };
564
565 // Change the text.
566 for mut text in &mut texts {
567 text.0 = String::new();
568 if !occlusion_culling_supported {
569 text.0
570 .push_str("Occlusion culling not supported on this platform");
571 continue;
572 }
573
574 let _ = writeln!(
575 &mut text.0,
576 "Occlusion culling {} (Press Space to toggle)",
577 if app_status.occlusion_culling {
578 "ON"
579 } else {
580 "OFF"
581 },
582 );
583
584 if !occlusion_culling_introspection_supported {
585 continue;
586 }
587
588 let _ = write!(
589 &mut text.0,
590 "{rendered_object_count}/{total_mesh_count} meshes rendered"
591 );
592 }
593}
594
595/// A system that reads the indirect parameters back from the GPU so that we can
596/// report how many meshes were culled.
597fn readback_indirect_parameters(
598 mut indirect_parameters_staging_buffers: ResMut<IndirectParametersStagingBuffers>,
599 saved_indirect_parameters: Res<SavedIndirectParameters>,
600) {
601 // If culling isn't supported on this platform, bail.
602 if !saved_indirect_parameters
603 .lock()
604 .unwrap()
605 .as_ref()
606 .unwrap()
607 .occlusion_culling_supported
608 {
609 return;
610 }
611
612 // Grab the staging buffers.
613 let (Some(data_buffer), Some(batch_sets_buffer)) = (
614 indirect_parameters_staging_buffers.data.take(),
615 indirect_parameters_staging_buffers.batch_sets.take(),
616 ) else {
617 return;
618 };
619
620 // Read the GPU buffers back.
621 let saved_indirect_parameters_0 = (**saved_indirect_parameters).clone();
622 let saved_indirect_parameters_1 = (**saved_indirect_parameters).clone();
623 readback_buffer::<IndirectParametersIndexed>(data_buffer, move |indirect_parameters| {
624 saved_indirect_parameters_0
625 .lock()
626 .unwrap()
627 .as_mut()
628 .unwrap()
629 .data = indirect_parameters.to_vec();
630 });
631 readback_buffer::<u32>(batch_sets_buffer, move |indirect_parameters_count| {
632 saved_indirect_parameters_1
633 .lock()
634 .unwrap()
635 .as_mut()
636 .unwrap()
637 .count = indirect_parameters_count[0];
638 });
639}1.0.0 · Sourcepub fn try_lock(
&self,
) -> Result<MutexGuard<'_, T>, TryLockError<MutexGuard<'_, T>>>
pub fn try_lock( &self, ) -> Result<MutexGuard<'_, T>, TryLockError<MutexGuard<'_, T>>>
Attempts to acquire this lock.
If the lock could not be acquired at this time, then Err is returned.
Otherwise, an RAII guard is returned. The lock will be unlocked when the
guard is dropped.
This function does not block.
§Errors
If another user of this mutex panicked while holding the mutex, then
this call will return the Poisoned error if the mutex would
otherwise be acquired. An acquired lock guard will be contained
in the returned error.
If the mutex could not be acquired because it is already locked, then
this call will return the WouldBlock error.
§Examples
use std::sync::{Arc, Mutex};
use std::thread;
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
thread::spawn(move || {
let mut lock = c_mutex.try_lock();
if let Ok(ref mut mutex) = lock {
**mutex = 10;
} else {
println!("try_lock failed");
}
}).join().expect("thread::spawn failed");
assert_eq!(*mutex.lock().unwrap(), 10);1.2.0 · Sourcepub fn is_poisoned(&self) -> bool
pub fn is_poisoned(&self) -> bool
Determines whether the mutex is poisoned.
If another thread is active, the mutex can still become poisoned at any
time. You should not trust a false value for program correctness
without additional synchronization.
§Examples
use std::sync::{Arc, Mutex};
use std::thread;
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
let _ = thread::spawn(move || {
let _lock = c_mutex.lock().unwrap();
panic!(); // the mutex gets poisoned
}).join();
assert_eq!(mutex.is_poisoned(), true);1.77.0 · Sourcepub fn clear_poison(&self)
pub fn clear_poison(&self)
Clear the poisoned state from a mutex.
If the mutex is poisoned, it will remain poisoned until this function is called. This allows recovering from a poisoned state and marking that it has recovered. For example, if the value is overwritten by a known-good value, then the mutex can be marked as un-poisoned. Or possibly, the value could be inspected to determine if it is in a consistent state, and if so the poison is removed.
§Examples
use std::sync::{Arc, Mutex};
use std::thread;
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
let _ = thread::spawn(move || {
let _lock = c_mutex.lock().unwrap();
panic!(); // the mutex gets poisoned
}).join();
assert_eq!(mutex.is_poisoned(), true);
let x = mutex.lock().unwrap_or_else(|mut e| {
**e.get_mut() = 1;
mutex.clear_poison();
e.into_inner()
});
assert_eq!(mutex.is_poisoned(), false);
assert_eq!(*x, 1);1.6.0 · Sourcepub fn into_inner(self) -> Result<T, PoisonError<T>>
pub fn into_inner(self) -> Result<T, PoisonError<T>>
Consumes this mutex, returning the underlying data.
§Errors
If another user of this mutex panicked while holding the mutex, then this call will return an error containing the underlying data instead.
§Examples
use std::sync::Mutex;
let mutex = Mutex::new(0);
assert_eq!(mutex.into_inner().unwrap(), 0);1.6.0 · Sourcepub fn get_mut(&mut self) -> Result<&mut T, PoisonError<&mut T>>
pub fn get_mut(&mut self) -> Result<&mut T, PoisonError<&mut T>>
Returns a mutable reference to the underlying data.
Since this call borrows the Mutex mutably, no actual locking needs to
take place – the mutable borrow statically guarantees no new locks can be acquired
while this reference exists. Note that this method does not clear any previous abandoned locks
(e.g., via forget() on a MutexGuard).
§Errors
If another user of this mutex panicked while holding the mutex, then this call will return an error containing a mutable reference to the underlying data instead.
§Examples
use std::sync::Mutex;
let mut mutex = Mutex::new(0);
*mutex.get_mut().unwrap() = 10;
assert_eq!(*mutex.lock().unwrap(), 10);Sourcepub const fn data_ptr(&self) -> *mut T
🔬This is a nightly-only experimental API. (mutex_data_ptr)
pub const fn data_ptr(&self) -> *mut T
mutex_data_ptr)Returns a raw pointer to the underlying data.
The returned pointer is always non-null and properly aligned, but it is the user’s responsibility to ensure that any reads and writes through it are properly synchronized to avoid data races, and that it is not read or written through after the mutex is dropped.
Trait Implementations§
Source§impl<C> ClockSequence for Mutex<C>where
C: ClockSequence + RefUnwindSafe,
impl<C> ClockSequence for Mutex<C>where
C: ClockSequence + RefUnwindSafe,
Source§type Output = <C as ClockSequence>::Output
type Output = <C as ClockSequence>::Output
Source§fn generate_sequence(
&self,
seconds: u64,
subsec_nanos: u32,
) -> <Mutex<C> as ClockSequence>::Output
fn generate_sequence( &self, seconds: u64, subsec_nanos: u32, ) -> <Mutex<C> as ClockSequence>::Output
Source§fn generate_timestamp_sequence(
&self,
seconds: u64,
subsec_nanos: u32,
) -> (<Mutex<C> as ClockSequence>::Output, u64, u32)
fn generate_timestamp_sequence( &self, seconds: u64, subsec_nanos: u32, ) -> (<Mutex<C> as ClockSequence>::Output, u64, u32)
Source§fn usable_bits(&self) -> usize
fn usable_bits(&self) -> usize
ClockSequence::generate_sequence
or ClockSequence::generate_timestamp_sequence. Read moreSource§impl<'de, T> Deserialize<'de> for Mutex<T>where
T: Deserialize<'de>,
impl<'de, T> Deserialize<'de> for Mutex<T>where
T: Deserialize<'de>,
Source§fn deserialize<D>(
deserializer: D,
) -> Result<Mutex<T>, <D as Deserializer<'de>>::Error>where
D: Deserializer<'de>,
fn deserialize<D>(
deserializer: D,
) -> Result<Mutex<T>, <D as Deserializer<'de>>::Error>where
D: Deserializer<'de>,
1.24.0 · Source§impl<T> From<T> for Mutex<T>
impl<T> From<T> for Mutex<T>
Source§fn from(t: T) -> Mutex<T>
fn from(t: T) -> Mutex<T>
Creates a new mutex in an unlocked state ready for use.
This is equivalent to Mutex::new.
Source§impl<'a, W> MakeWriter<'a> for Mutex<W>where
W: Write + 'a,
impl<'a, W> MakeWriter<'a> for Mutex<W>where
W: Write + 'a,
Source§type Writer = MutexGuardWriter<'a, W>
type Writer = MutexGuardWriter<'a, W>
io::Write implementation returned by make_writer.Source§fn make_writer(&'a self) -> <Mutex<W> as MakeWriter<'a>>::Writer
fn make_writer(&'a self) -> <Mutex<W> as MakeWriter<'a>>::Writer
Source§impl<T> Serialize for Mutex<T>
impl<T> Serialize for Mutex<T>
Source§fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>where
S: Serializer,
fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>where
S: Serializer,
Source§impl SubscriberList for Mutex<HashSet<ReactiveContext>>
impl SubscriberList for Mutex<HashSet<ReactiveContext>>
Source§fn add(&self, subscriber: ReactiveContext)
fn add(&self, subscriber: ReactiveContext)
Source§fn remove(&self, subscriber: &ReactiveContext)
fn remove(&self, subscriber: &ReactiveContext)
Source§fn visit(&self, f: &mut dyn FnMut(&ReactiveContext))
fn visit(&self, f: &mut dyn FnMut(&ReactiveContext))
impl<T> RefUnwindSafe for Mutex<T>where
T: ?Sized,
impl<T> Send for Mutex<T>
T must be Send for a Mutex to be Send because it is possible to acquire
the owned T from the Mutex via into_inner.
impl<T> Sync for Mutex<T>
T must be Send for Mutex to be Sync.
This ensures that the protected data can be accessed safely from multiple threads
without causing data races or other unsafe behavior.
Mutex<T> provides mutable access to T to one thread at a time. However, it’s essential
for T to be Send because it’s not safe for non-Send structures to be accessed in
this manner. For instance, consider Rc, a non-atomic reference counted smart pointer,
which is not Send. With Rc, we can have multiple copies pointing to the same heap
allocation with a non-atomic reference count. If we were to use Mutex<Rc<_>>, it would
only protect one instance of Rc from shared access, leaving other copies vulnerable
to potential data races.
Also note that it is not necessary for T to be Sync as &T is only made available
to one thread at a time if T is not Sync.
impl<T> UnwindSafe for Mutex<T>where
T: ?Sized,
Auto Trait Implementations§
Blanket Implementations§
Source§impl<T, U> AsBindGroupShaderType<U> for T
impl<T, U> AsBindGroupShaderType<U> for T
Source§fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
T ShaderType for self. When used in AsBindGroup
derives, it is safe to assume that all images in self exist.Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<'de, T> DynamicDeserialize<'de> for Twhere
T: Type + Deserialize<'de>,
impl<'de, T> DynamicDeserialize<'de> for Twhere
T: Type + Deserialize<'de>,
Source§type Deserializer = PhantomData<T>
type Deserializer = PhantomData<T>
Source§fn deserializer_for_signature(
signature: &Signature,
) -> Result<<T as DynamicDeserialize<'de>>::Deserializer, Error>
fn deserializer_for_signature( signature: &Signature, ) -> Result<<T as DynamicDeserialize<'de>>::Deserializer, Error>
Source§impl<T> DynamicType for T
impl<T> DynamicType for T
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T> FromWorld for Twhere
T: Default,
impl<T> FromWorld for Twhere
T: Default,
Source§fn from_world(_world: &mut World) -> T
fn from_world(_world: &mut World) -> T
Creates Self using default().
Source§impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
Source§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
Source§impl<T> InitializeFromFunction<T> for T
impl<T> InitializeFromFunction<T> for T
Source§fn initialize_from_function(f: fn() -> T) -> T
fn initialize_from_function(f: fn() -> T) -> T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoResult<T> for T
impl<T> IntoResult<T> for T
Source§fn into_result(self) -> Result<T, RunSystemError>
fn into_result(self) -> Result<T, RunSystemError>
Source§impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
fn into_sample(self) -> T
Source§impl<'a, M> MakeWriterExt<'a> for Mwhere
M: MakeWriter<'a>,
impl<'a, M> MakeWriterExt<'a> for Mwhere
M: MakeWriter<'a>,
Source§fn with_max_level(self, level: Level) -> WithMaxLevel<Self>where
Self: Sized,
fn with_max_level(self, level: Level) -> WithMaxLevel<Self>where
Self: Sized,
self and returns a MakeWriter that will only write output
for events at or below the provided verbosity Level. For instance,
Level::TRACE is considered to be _more verbosethanLevel::INFO`. Read moreSource§fn with_min_level(self, level: Level) -> WithMinLevel<Self>where
Self: Sized,
fn with_min_level(self, level: Level) -> WithMinLevel<Self>where
Self: Sized,
self and returns a MakeWriter that will only write output
for events at or above the provided verbosity Level. Read moreSource§fn with_filter<F>(self, filter: F) -> WithFilter<Self, F>
fn with_filter<F>(self, filter: F) -> WithFilter<Self, F>
self with a predicate that takes a span or event’s Metadata
and returns a bool. The returned MakeWriter’s
MakeWriter::make_writer_for method will check the predicate to
determine if a writer should be produced for a given span or event. Read moreSource§fn and<B>(self, other: B) -> Tee<Self, B> ⓘwhere
Self: Sized,
B: MakeWriter<'a>,
fn and<B>(self, other: B) -> Tee<Self, B> ⓘwhere
Self: Sized,
B: MakeWriter<'a>,
self with another type implementing MakeWriter, returning
a new MakeWriter that produces writers that write to both
outputs. Read moreSource§fn or_else<W, B>(self, other: B) -> OrElse<Self, B>
fn or_else<W, B>(self, other: B) -> OrElse<Self, B>
self with another type implementing MakeWriter, returning
a new MakeWriter that calls other’s make_writer if self’s
make_writer returns OptionalWriter::none. Read moreSource§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<R, P> ReadPrimitive<R> for P
impl<R, P> ReadPrimitive<R> for P
Source§fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
ReadEndian::read_from_little_endian().Source§impl<T> Serialize for T
impl<T> Serialize for T
fn erased_serialize(&self, serializer: &mut dyn Serializer) -> Result<(), Error>
fn do_erased_serialize( &self, serializer: &mut dyn Serializer, ) -> Result<(), ErrorImpl>
Source§impl<Ret> SpawnIfAsync<(), Ret> for Ret
impl<Ret> SpawnIfAsync<(), Ret> for Ret
Source§impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
Source§fn super_from(input: T) -> O
fn super_from(input: T) -> O
Source§impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
Source§fn super_into(self) -> O
fn super_into(self) -> O
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.