AccountsSharedData: data copy on write (#15800)

* Arc<AccountData>

* try custom serializer

* adapt test from Behzad's change

* clippy

* simplify serialization

* remove abi example derive

* refactor 'take'

* remove serialization

* remove serialize calls

* remove account_data

* remove intos

* remove left over file
This commit is contained in:
Jeff Washington (jwash)
2021-03-23 15:19:31 -05:00
committed by GitHub
parent 6f5d8d18e9
commit 2d24d13046
3 changed files with 22 additions and 25 deletions

View File

@ -1559,8 +1559,9 @@ mod tests {
} }
fn truncate_data(account: &mut AccountSharedData, len: usize) { fn truncate_data(account: &mut AccountSharedData, len: usize) {
// when account data becomes copy on write, this operation will be more complicated let mut data = account.data.to_vec();
account.data.truncate(len); data.truncate(len);
account.set_data(data);
} }
#[test] #[test]

View File

@ -413,7 +413,9 @@ pub mod tests {
// Vote account too big // Vote account too big
let cache_data = vote_account.data().to_vec(); let cache_data = vote_account.data().to_vec();
vote_account.data.push(0); let mut pushed = vote_account.data.to_vec();
pushed.push(0);
vote_account.set_data(pushed);
stakes.store(&vote_pubkey, &vote_account, true, true); stakes.store(&vote_pubkey, &vote_account, true, true);
{ {

View File

@ -1,6 +1,6 @@
use crate::{clock::Epoch, pubkey::Pubkey}; use crate::{clock::Epoch, pubkey::Pubkey};
use solana_program::{account_info::AccountInfo, sysvar::Sysvar}; use solana_program::{account_info::AccountInfo, sysvar::Sysvar};
use std::{cell::Ref, cell::RefCell, cmp, fmt, rc::Rc}; use std::{cell::Ref, cell::RefCell, cmp, fmt, rc::Rc, sync::Arc};
/// An Account with data that is stored on chain /// An Account with data that is stored on chain
#[repr(C)] #[repr(C)]
@ -22,16 +22,14 @@ pub struct Account {
} }
/// An Account with data that is stored on chain /// An Account with data that is stored on chain
/// This will become a new in-memory representation of the 'Account' struct data. /// This will be the in-memory representation of the 'Account' struct data.
/// The existing 'Account' structure cannot easily change due to downstream projects. /// The existing 'Account' structure cannot easily change due to downstream projects.
/// This struct will shortly rely on something like the ReadableAccount trait for access to the fields. #[derive(PartialEq, Eq, Clone, Default, AbiExample)]
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Default, AbiExample)]
pub struct AccountSharedData { pub struct AccountSharedData {
/// lamports in the account /// lamports in the account
pub lamports: u64, pub lamports: u64,
/// data held in this account /// data held in this account
#[serde(with = "serde_bytes")] pub data: Arc<Vec<u8>>,
pub data: Vec<u8>, // will be: Arc<Vec<u8>>,
/// the program that owns this account. If executable, the program that loads this account. /// the program that owns this account. If executable, the program that loads this account.
pub owner: Pubkey, pub owner: Pubkey,
/// this account's data contains a loaded program (and is now read-only) /// this account's data contains a loaded program (and is now read-only)
@ -52,10 +50,11 @@ pub fn accounts_equal<T: ReadableAccount, U: ReadableAccount>(me: &T, other: &U)
} }
impl From<AccountSharedData> for Account { impl From<AccountSharedData> for Account {
fn from(other: AccountSharedData) -> Self { fn from(mut other: AccountSharedData) -> Self {
let account_data = Arc::make_mut(&mut other.data);
Self { Self {
lamports: other.lamports, lamports: other.lamports,
data: other.data, data: std::mem::take(account_data),
owner: other.owner, owner: other.owner,
executable: other.executable, executable: other.executable,
rent_epoch: other.rent_epoch, rent_epoch: other.rent_epoch,
@ -67,7 +66,7 @@ impl From<Account> for AccountSharedData {
fn from(other: Account) -> Self { fn from(other: Account) -> Self {
Self { Self {
lamports: other.lamports, lamports: other.lamports,
data: other.data, data: Arc::new(other.data),
owner: other.owner, owner: other.owner,
executable: other.executable, executable: other.executable,
rent_epoch: other.rent_epoch, rent_epoch: other.rent_epoch,
@ -154,7 +153,8 @@ impl WritableAccount for AccountSharedData {
self.lamports = lamports; self.lamports = lamports;
} }
fn data_as_mut_slice(&mut self) -> &mut [u8] { fn data_as_mut_slice(&mut self) -> &mut [u8] {
&mut self.data let data = Arc::make_mut(&mut self.data);
&mut data[..]
} }
fn set_owner(&mut self, owner: Pubkey) { fn set_owner(&mut self, owner: Pubkey) {
self.owner = owner; self.owner = owner;
@ -174,7 +174,7 @@ impl WritableAccount for AccountSharedData {
) -> Self { ) -> Self {
AccountSharedData { AccountSharedData {
lamports, lamports,
data, data: Arc::new(data),
owner, owner,
executable, executable,
rent_epoch, rent_epoch,
@ -394,22 +394,16 @@ impl Account {
} }
impl AccountSharedData { impl AccountSharedData {
/// make account's data equal to 'data'. This may require resizing and copying data.
pub fn set_data_from_slice(&mut self, data: &[u8]) { pub fn set_data_from_slice(&mut self, data: &[u8]) {
let len = self.data.len(); let len = self.data.len();
let len_different = len != data.len(); let len_different = len != data.len();
if len_different { let different = len_different || data != &self.data[..];
// if the resize causes a reallocation and copy, it would be better to create a new copy of the final data if different {
// rather than resize (+ copy current) and then copy over below. self.data = Arc::new(data.to_vec());
// however, the implementation of account's data is soon to be copy on write, so the tradeoffs will soon be different.
self.data.resize(data.len(), 0);
} }
// we could compare here to determine whether we need to modify the original data or not. In the current implementation, that would
// not make a positive difference.
self.data.copy_from_slice(data);
} }
pub fn set_data(&mut self, data: Vec<u8>) { pub fn set_data(&mut self, data: Vec<u8>) {
self.data = data; self.data = Arc::new(data);
} }
pub fn new(lamports: u64, space: usize, owner: &Pubkey) -> Self { pub fn new(lamports: u64, space: usize, owner: &Pubkey) -> Self {
shared_new(lamports, space, owner) shared_new(lamports, space, owner)
@ -691,7 +685,7 @@ pub mod tests {
account1.data_as_mut_slice()[0] = account1.data[0] + 1; account1.data_as_mut_slice()[0] = account1.data[0] + 1;
} else if pass == 3 { } else if pass == 3 {
account_expected.data[0] += 1; account_expected.data[0] += 1;
account2.data[0] += 1; account2.data_as_mut_slice()[0] += 1;
} }
} else if field_index == 2 { } else if field_index == 2 {
if pass == 0 { if pass == 0 {