পাঠ ১৬.৪

Send এবং Sync দিয়ে concurrency বাড়ানো

Extensible Concurrency with the Send and Sync Traits

মজার ব্যাপার — এই chapter-এ যত concurrency feature দেখলে, তার প্রায় সবগুলো language-এর না, standard library-র অংশ। তুমি নিজেও নতুন concurrency primitive লিখতে পারো, বা crate ecosystem থেকে নিতে পারো। কিন্তু দু'টো জিনিস language-এর — marker trait Send আর Sync

Send — ownership thread-এ পাঠানো

কোনো type Send implement করলে — সেটার ownership এক thread থেকে আরেক thread-এ transfer করা safe। প্রায় সব Rust type Send

উল্লেখযোগ্য ব্যতিক্রম:

  • Rc<T>Send না। দু'টা thread একই Rc clone করে ref count update করতে গেলে race হত।
  • Raw pointer (*const T, *mut T) — Send না। Chapter 20-এ unsafe Rust-এ আবার আসবে।

আগের পাঠে দেখেছ — Rc<Mutex<i32>> spawn-এ পাঠালে error:

compile errortext
the trait `Send` is not implemented for `Rc<Mutex<i32>>`

সমাধান — Arc<T>, যেটা Send implement করে।

Auto-implementation: যেকোনো type যা পুরোপুরি Send type দিয়ে গঠিত — সেটাও automatic Send। প্রায় সব primitive type Send

Sync — একাধিক thread থেকে reference

কোনো type Sync implement করলে — তার immutable reference (&T) একাধিক thread থেকে access করা safe।

Formal definition — T তখনই Sync, যখন &T Send। অর্থাৎ reference-কে নিরাপদে অন্য thread-এ পাঠানো যায়।

উদাহরণ:

  • সব primitive — Sync
  • Rc<T>Sync না (একই reason)।
  • RefCell<T> এবং Cell family — Sync না। Runtime borrow check thread-safe না।
  • Mutex<T>Sync। তাই multi-threaded shared access-এ এটাই ব্যবহার করি।

নিজে implement করা — unsafe

SendSync দু'টোই marker trait — কোনো method নেই। শুধু compile-time invariant track করে।

যেহেতু Send/Sync components থেকে গড়া type automatic এই trait পায় — manually implement করার দরকার সাধারণত হয় না।

কিন্তু যদি raw pointer-জাতীয় non-Send/non-Sync জিনিস ব্যবহার করো, এবং নিজে নিশ্চিত হও যে invariant ঠিক আছে — তাহলে unsafe impl দিয়ে hand-implement করতে হয়:

unsafe impl Send for MyType {}
unsafe impl Sync for MyType {}

unsafe মানে — তুমি promise করছ যে type-টা সত্যিই thread-এর মধ্যে move/share করা safe। Compiler আর verify করতে পারবে না — সেই দায়িত্ব তোমার।

গভীরে যেতে চাইলে The Rustonomicon দেখো — Rust-এর unsafe ও concurrent invariant-এর reference।

কেন এটা "extensible"

Rust-এর concurrency story-র চমৎকার দিক — language শুধু Send/Sync ও কয়েকটা primitive দিয়েছে। বাকি সব — channel, mutex, atomic, async runtime, work-stealing scheduler — library/crate-এ বানানো। এই minimal বুনিয়াদের উপর community নতুন concurrency abstraction তৈরি করতে পারে, এবং type system নিজে থেকেই data race প্রতিরোধ করে।

এই পাঠ থেকে যা শিখলে

  • Send — ownership thread-এ পাঠানো safe; প্রায় সব type-এ auto।
  • Sync&T অন্য thread-এ পাঠানো safe; অর্থাৎ shared immutable access।
  • Rc, RefCell, Cell — Send/Sync না; raw pointer-ও না।
  • Arc, Mutex, primitive type — Send এবং Sync।
  • Marker trait — method নেই, শুধু compile-time invariant; component সব Send/Sync হলে auto-implementation।
  • Manual implementation unsafe impl দিয়ে — তুমি promise করছ invariant ঠিক রাখবে।