xtd 0.2.0
Loading...
Searching...
No Matches
xtd::threading::interlocked Class Reference
Inheritance diagram for xtd::threading::interlocked:
xtd::static_object

Definition

Provides atomic operations for variables that are shared by multiple threads.

Provides atomic operations for variables that are shared by multiple threads.
Definition interlocked.h:38
#define static_
This keyword is use to represent a static object. A static object can't be instantiated (constructors...
Definition static.h:37
#define core_export_
Define shared library export.
Definition core_export.h:13
Inheritance
xtd::static_objectxtd::threading::interlocked
Header
#include <xtd/threading/interlocked>
Namespace
xtd::threading
Library
xtd.core
Remarks
The methods of this class help protect against errors that can occur when the scheduler switches contexts while a thread is updating a variable that can be accessed by other threads, or when two threads are executing concurrently on separate processors. The members of this class do not throw exceptions.
The xtd::threading::interlocked::increment and xtd::threading::interlocked::decrement methods increment or decrement a variable and store the resulting value in a single operation. On most computers, incrementing a variable is not an atomic operation, requiring the following steps:
1. Load a value from an instance variable into a register.
2. Increment or decrement the value.
3. Store the value in the instance variable.
If you do not use xtd::threading::interlocked::increment and xtd::threading::interlocked::decrement, a thread can be preempted after executing the first two steps. Another thread can then execute all three steps. When the first thread resumes execution, it overwrites the value in the instance variable, and the effect of the increment or decrement performed by the second thread is lost.
The xtd::threading::exchange method atomically exchanges the values of the specified variables. The compare_exchange method combines two operations: comparing two values and storing a third value in one of the variables, based on the outcome of the comparison. The compare and exchange operations are performed as an atomic operation.
Examples
The following code example shows a thread-safe resource locking mechanism.
#include <xtd/threading/interlocked>
#include <xtd/threading/thread>
#include <xtd/console>
#include <xtd/random>
#include <xtd/startup>
using namespace xtd;
using namespace xtd::threading;
namespace interlocked_example {
struct my_thread {
ustring name;
};
class my_interlocked_exchange_class {
private:
//0 for False, 1 for True.
inline static int using_resource = 0;
static constexpr int num_thread_iterations = 5;
public:
// The main entry point for the application.
static auto main() {
auto my_threads = std::array<my_thread, 10> {};
auto rnd = xtd::random {};
for (auto index = 0ul; index < my_threads.size(); ++index) {
my_threads[index].name = ustring::format("Thread{}", index + 1);
//Wait a random amount of time before starting next thread.
thread::sleep(rnd.next(0, 1000));
my_threads[index].thread = thread {my_thread_proc};
my_threads[index].thread.start(my_threads[index].name);
}
for (auto index = 0ul; index < my_threads.size(); ++index)
my_threads[index].thread.join();
}
static void my_thread_proc(std::any name) {
for (auto index = 0; index < num_thread_iterations; ++index) {
use_resource(as<ustring>(name));
//Wait 1 second before next attempt.
thread::sleep(1000);
}
}
//A simple method that denies reentrancy.
static auto use_resource(const ustring& name)->bool {
//0 indicates that the method is not in use.
if (0 == interlocked::exchange(using_resource, 1)) {
console::write_line("{} acquired the lock", name);
//Code to access a resource that is not thread safe would go here.
//Simulate some work
thread::sleep(500);
console::write_line("{} exiting lock", name);
//Release the lock
interlocked::exchange(using_resource, 0);
return true;
} else {
console::write_line(" {} was denied the lock", name);
return false;
}
}
};
}
startup_(interlocked_example::my_interlocked_exchange_class::main);
// This example produces output similar to the following:
//
// Thread1 acquired the lock
// Thread1 exiting lock
// Thread2 acquired the lock
// Thread3 was denied the lock
// Thread2 exiting lock
// Thread1 acquired the lock
// Thread4 was denied the lock
// Thread5 was denied the lock
// Thread1 exiting lock
// Thread6 acquired the lock
// Thread3 was denied the lock
// Thread2 was denied the lock
// Thread7 was denied the lock
// Thread6 exiting lock
// Thread4 acquired the lock
// Thread5 was denied the lock
// Thread1 was denied the lock
// Thread4 exiting lock
// Thread8 acquired the lock
// Thread3 was denied the lock
// Thread2 was denied the lock
// Thread7 was denied the lock
// Thread6 was denied the lock
// Thread9 was denied the lock
// Thread8 exiting lock
// Thread5 acquired the lock
// Thread1 was denied the lock
// Thread4 was denied the lock
// Thread3 was denied the lock
// Thread10 was denied the lock
// Thread2 was denied the lock
// Thread7 was denied the lock
// Thread5 exiting lock
// Thread6 acquired the lock
// Thread9 was denied the lock
// Thread8 was denied the lock
// Thread1 was denied the lock
// Thread6 exiting lock
// Thread4 acquired the lock
// Thread3 was denied the lock
// Thread10 was denied the lock
// Thread2 was denied the lock
// Thread7 was denied the lock
// Thread5 was denied the lock
// Thread4 exiting lock
// Thread9 acquired the lock
// Thread8 was denied the lock
// Thread6 was denied the lock
// Thread9 exiting lock
// Thread10 acquired the lock
// Thread7 was denied the lock
// Thread5 was denied the lock
// Thread4 was denied the lock
// Thread8 was denied the lock
// Thread10 exiting lock
// Thread6 acquired the lock
// Thread9 was denied the lock
// Thread6 exiting lock
// Thread8 acquired the lock
// Thread10 was denied the lock
// Thread9 was denied the lock
// Thread8 exiting lock
// Thread10 acquired the lock
// Thread10 exiting lock
Represents a pseudo-random number generator, a device that produces a sequence of numbers that meet c...
Definition random.h:40
Creates and controls a thread, sets its priority, and gets its status.
Definition thread.h:41
thread(const xtd::threading::parameterized_thread_start &start)
Initializes a new instance of the xtd::threading::thread class, specifying a delegate that allows an ...
void join()
Blocks the calling thread until this thread object terminates, while continuing to perform standard C...
Represents text as a sequence of UTF-8 code units.
Definition ustring.h:47
#define startup_(main_method)
Defines the entry point to be called when the application loads. Generally this is set either to the ...
Definition startup.h:166
The xtd::threading namespace provides classes and interfaces that enable multithreaded programming....
Definition abandoned_mutex_exception.h:10
The xtd namespace contains all fundamental classes to access Hardware, Os, System,...
Definition xtd_about_box.h:10

Public Static Methods

static int32 add (int32 &location, int32 value) noexcept
 Adds two 32-bit integers and replaces the first integer with the sum, as an atomic operation.
 
static int64 add (int64 &location, int64 value) noexcept
 Adds two 64-bit integers and replaces the first integer with the sum, as an atomic operation.
 
static double compare_exchange (double &location, double value, double comparand) noexcept
 Compares two Double for equality and, if they are equal, replaces one of the values.
 
static int32 compare_exchange (int32 &location, int32 value, int32 comparand) noexcept
 Compares two 32-bit signed integers for equality and, if they are equal, replaces one of the values.
 
static int64 compare_exchange (int64 &location, int64 value, int64 comparand) noexcept
 Compares two 64-bit signed integers for equality and, if they are equal, replaces one of the values.
 
static void * compare_exchange (void *&location, void *value, void *comparand) noexcept
 Compares two platform-specific handles or pointers for equality and, if they are equal, replaces one of them.
 
template<typename type_t >
static type_t compare_exchange (object &location, const object &value, const object &comparand) noexcept
 Compares two objects for equality and, if they are equal, replaces one of them.
 
template<typename type_t >
static type_t compare_exchange (type_t &location, type_t value, type_t comparand) noexcept
 Compares two instances of the specified reference type type_t for equality and, if they are equal, replaces one of them.
 
static float compare_exchange (float &location, float value, float comparand) noexcept
 Compares two Single for equality and, if they are equal, replaces one of the values.
 
static int32 decrement (int32 &location) noexcept
 Decrements a specified variable and stores the result, as an atomic operation.
 
static int64 decrement (int64 &location) noexcept
 Decrements a specified variable and stores the result, as an atomic operation.
 
template<typename type_t >
static type_t exchange (type_t &location, type_t value)
 Sets a variable of the specified type type_t to a specified value and returns the original value, as an atomic operation.
 
static double exchange (double &location, double value) noexcept
 Sets a double-precision floating point number to a specified value and returns the original value, as an atomic operation.
 
static int32 exchange (int32 &location, int32 value) noexcept
 Sets a 32-bit signed integer to a specified value and returns the original value, as an atomic operation.
 
static int64 exchange (int64 &location, int64 value) noexcept
 Sets a 64-bit signed integer to a specified value and returns the original value, as an atomic operation.
 
static void * exchange (void *&location, void *value) noexcept
 Sets a platform-specific handles or pointers to a specified value and returns the original value, as an atomic operation.
 
template<typename type_t >
static type_t exchange (object &location, const object &value) noexcept
 Sets an object to a specified value and returns the original value, as an atomic operation.
 
static float exchange (float &location, float value) noexcept
 Sets a double-precision floating point number to a specified value and returns the original value, as an atomic operation.
 
static int32 increment (int32 &location) noexcept
 Increments a specified variable and stores the result, as an atomic operation.
 
static int64 increment (int64 &location) noexcept
 Increments a specified variable and stores the result, as an atomic operation.
 
static void memory_barrier () noexcept
 Synchronizes memory access as follows: The processor that executes the current thread cannot reorder instructions in such a way that memory accesses before the call to xtd::threading::interlocked::memory_barrier execute after memory accesses that follow the call to xtd::threading::interlocked::memory_barrier.
 
static int64 read (int64 &location) noexcept
 Returns a 64-bit value, loaded as an atomic operation.
 

Member Function Documentation

◆ add() [1/2]

static int32 xtd::threading::interlocked::add ( int32 location,
int32  value 
)
staticnoexcept

Adds two 32-bit integers and replaces the first integer with the sum, as an atomic operation.

Parameters
locationA variable containing the first value to be added. The sum of the two values is stored in location.
valueThe value to be added to the integer at location.
Returns
int32 The new value stored at location.

◆ add() [2/2]

static int64 xtd::threading::interlocked::add ( int64 location,
int64  value 
)
staticnoexcept

Adds two 64-bit integers and replaces the first integer with the sum, as an atomic operation.

Parameters
locationA variable containing the first value to be added. The sum of the two values is stored in location.
valueThe value to be added to the integer at location.
Returns
int64 The new value stored at location.

◆ compare_exchange() [1/7]

static double xtd::threading::interlocked::compare_exchange ( double &  location,
double  value,
double  comparand 
)
staticnoexcept

Compares two Double for equality and, if they are equal, replaces one of the values.

Parameters
locationThe destination, whose value is compared with comparand and possibly replaced.
valueThe value that replaces the destination value if the comparison results in equality.
comparandThe value that is compared to the value at location.
Returns
The original value in location.

◆ compare_exchange() [2/7]

static float xtd::threading::interlocked::compare_exchange ( float &  location,
float  value,
float  comparand 
)
staticnoexcept

Compares two Single for equality and, if they are equal, replaces one of the values.

Parameters
locationThe destination, whose value is compared with comparand and possibly replaced.
valueThe value that replaces the destination value if the comparison results in equality.
comparandThe value that is compared to the value at location.
Returns
The original value in location.

◆ compare_exchange() [3/7]

static int32 xtd::threading::interlocked::compare_exchange ( int32 location,
int32  value,
int32  comparand 
)
staticnoexcept

Compares two 32-bit signed integers for equality and, if they are equal, replaces one of the values.

Parameters
locationThe destination, whose value is compared with Comparand and possibly replaced.
valueThe value that replaces the destination value if the comparison results in equality.
ComparandThe value that is compared to the value at location.
Returns
The original value in location.

◆ compare_exchange() [4/7]

static int64 xtd::threading::interlocked::compare_exchange ( int64 location,
int64  value,
int64  comparand 
)
staticnoexcept

Compares two 64-bit signed integers for equality and, if they are equal, replaces one of the values.

Parameters
locationThe destination, whose value is compared with comparand and possibly replaced.
valueThe value that replaces the destination value if the comparison results in equality.
comparandThe value that is compared to the value at location.
Returns
The original value in location.

◆ compare_exchange() [5/7]

template<typename type_t >
static type_t xtd::threading::interlocked::compare_exchange ( object location,
const object value,
const object comparand 
)
inlinestaticnoexcept

Compares two objects for equality and, if they are equal, replaces one of them.

Parameters
locationThe destination, whose value is compared with comparand and possibly replaced.
valueThe value that replaces the destination value if the comparison results in equality.
comparandThe value that is compared to the value at location.
Returns
The original value in location.

◆ compare_exchange() [6/7]

template<typename type_t >
static type_t xtd::threading::interlocked::compare_exchange ( type_t &  location,
type_t  value,
type_t  comparand 
)
inlinestaticnoexcept

Compares two instances of the specified reference type type_t for equality and, if they are equal, replaces one of them.

Parameters
locationThe destination, whose value is compared with comparand and possibly replaced.
valueThe value that replaces the destination value if the comparison results in equality.
comparandThe value that is compared to the value at location.
Returns
The original value in location.

◆ compare_exchange() [7/7]

static void * xtd::threading::interlocked::compare_exchange ( void *&  location,
void *  value,
void *  comparand 
)
staticnoexcept

Compares two platform-specific handles or pointers for equality and, if they are equal, replaces one of them.

Parameters
locationThe destination, whose value is compared with comparand and possibly replaced.
valueThe value that replaces the destination value if the comparison results in equality.
comparandThe value that is compared to the value at location.
Returns
The original value in location.

◆ decrement() [1/2]

static int32 xtd::threading::interlocked::decrement ( int32 location)
staticnoexcept

Decrements a specified variable and stores the result, as an atomic operation.

Parameters
locationThe variable whose value is to be decremented.
Returns
The decremented value.
Examples
The following code example shows a thread-safe way to increment and decrement an integer value. SafeInstanceCount will always be zero. However, UnsafeInstanceCount will not necessarily be zero because a race condition occurs between incrementing and decrementing the count. This effect is especially marked on a multiprocessor computer.
#include <xtd/threading/interlocked>
#include <xtd/threading/thread>
#include <xtd/console>
#include <xtd/startup>
#include <vector>
using namespace xtd;
using namespace xtd::threading;
namespace interlocked_decrement_example {
class my_interlocked_decrement_class {
private:
class count_class {
public:
inline static int unsafe_instance_count = 0;
inline static int safe_instance_count = 0;
count_class() {
unsafe_instance_count++;
interlocked::increment(safe_instance_count);
}
~count_class() {
unsafe_instance_count--;
interlocked::decrement(safe_instance_count);
}
};
public:
static auto main() {
auto thread1 = thread {thread_method};
auto thread2 = thread {thread_method};
thread1.start();
thread2.start();
thread1.join();
thread2.join();
console::write_line("unsafe_instance_count: {}\nsafe_instance_count: {}", count_class::unsafe_instance_count, count_class::safe_instance_count);
}
private:
static void thread_method() {
auto cc = count_class {};
// Create 100000 instances of count_class.
for (auto i = 0; i < 100000; ++i)
cc = count_class {};
}
};
}
startup_(interlocked_decrement_example::my_interlocked_decrement_class::main);
// This code produces the following output:
//
// unsafe_instance_count: 253
// safe_instance_count: 0
void start()
Causes the operating system to change the state of the current instance to xtd::threading::thread_sta...
@ i
The I key.

◆ decrement() [2/2]

static int64 xtd::threading::interlocked::decrement ( int64 location)
staticnoexcept

Decrements a specified variable and stores the result, as an atomic operation.

Parameters
locationThe variable whose value is to be decremented.
Returns
The decremented value.

◆ exchange() [1/7]

static double xtd::threading::interlocked::exchange ( double &  location,
double  value 
)
staticnoexcept

Sets a double-precision floating point number to a specified value and returns the original value, as an atomic operation.

Parameters
locationThe variable to set to the specified value.
valueThe value to which the location_d parameter is set.
Returns
The original value of location_d.

◆ exchange() [2/7]

static float xtd::threading::interlocked::exchange ( float &  location,
float  value 
)
staticnoexcept

Sets a double-precision floating point number to a specified value and returns the original value, as an atomic operation.

Parameters
locationThe variable to set to the specified value.
valueThe value to which the location_f parameter is set.
Returns
The original value of location_f.

◆ exchange() [3/7]

static int32 xtd::threading::interlocked::exchange ( int32 location,
int32  value 
)
staticnoexcept

Sets a 32-bit signed integer to a specified value and returns the original value, as an atomic operation.

Parameters
locationThe variable to set to the specified value.
valueThe value to which the location parameter is set.
Returns
The original value of location.
Examples
The following code example shows a thread-safe resource locking mechanism.
#include <xtd/threading/interlocked>
#include <xtd/threading/thread>
#include <xtd/console>
#include <xtd/random>
#include <xtd/startup>
using namespace xtd;
using namespace xtd::threading;
namespace interlocked_example {
struct my_thread {
ustring name;
};
class my_interlocked_exchange_class {
private:
//0 for False, 1 for True.
inline static int using_resource = 0;
static constexpr int num_thread_iterations = 5;
public:
// The main entry point for the application.
static auto main() {
auto my_threads = std::array<my_thread, 10> {};
auto rnd = xtd::random {};
for (auto index = 0ul; index < my_threads.size(); ++index) {
my_threads[index].name = ustring::format("Thread{}", index + 1);
//Wait a random amount of time before starting next thread.
thread::sleep(rnd.next(0, 1000));
my_threads[index].thread = thread {my_thread_proc};
my_threads[index].thread.start(my_threads[index].name);
}
for (auto index = 0ul; index < my_threads.size(); ++index)
my_threads[index].thread.join();
}
static void my_thread_proc(std::any name) {
for (auto index = 0; index < num_thread_iterations; ++index) {
use_resource(as<ustring>(name));
//Wait 1 second before next attempt.
thread::sleep(1000);
}
}
//A simple method that denies reentrancy.
static auto use_resource(const ustring& name)->bool {
//0 indicates that the method is not in use.
if (0 == interlocked::exchange(using_resource, 1)) {
console::write_line("{} acquired the lock", name);
//Code to access a resource that is not thread safe would go here.
//Simulate some work
thread::sleep(500);
console::write_line("{} exiting lock", name);
//Release the lock
interlocked::exchange(using_resource, 0);
return true;
} else {
console::write_line(" {} was denied the lock", name);
return false;
}
}
};
}
startup_(interlocked_example::my_interlocked_exchange_class::main);
// This example produces output similar to the following:
//
// Thread1 acquired the lock
// Thread1 exiting lock
// Thread2 acquired the lock
// Thread3 was denied the lock
// Thread2 exiting lock
// Thread1 acquired the lock
// Thread4 was denied the lock
// Thread5 was denied the lock
// Thread1 exiting lock
// Thread6 acquired the lock
// Thread3 was denied the lock
// Thread2 was denied the lock
// Thread7 was denied the lock
// Thread6 exiting lock
// Thread4 acquired the lock
// Thread5 was denied the lock
// Thread1 was denied the lock
// Thread4 exiting lock
// Thread8 acquired the lock
// Thread3 was denied the lock
// Thread2 was denied the lock
// Thread7 was denied the lock
// Thread6 was denied the lock
// Thread9 was denied the lock
// Thread8 exiting lock
// Thread5 acquired the lock
// Thread1 was denied the lock
// Thread4 was denied the lock
// Thread3 was denied the lock
// Thread10 was denied the lock
// Thread2 was denied the lock
// Thread7 was denied the lock
// Thread5 exiting lock
// Thread6 acquired the lock
// Thread9 was denied the lock
// Thread8 was denied the lock
// Thread1 was denied the lock
// Thread6 exiting lock
// Thread4 acquired the lock
// Thread3 was denied the lock
// Thread10 was denied the lock
// Thread2 was denied the lock
// Thread7 was denied the lock
// Thread5 was denied the lock
// Thread4 exiting lock
// Thread9 acquired the lock
// Thread8 was denied the lock
// Thread6 was denied the lock
// Thread9 exiting lock
// Thread10 acquired the lock
// Thread7 was denied the lock
// Thread5 was denied the lock
// Thread4 was denied the lock
// Thread8 was denied the lock
// Thread10 exiting lock
// Thread6 acquired the lock
// Thread9 was denied the lock
// Thread6 exiting lock
// Thread8 acquired the lock
// Thread10 was denied the lock
// Thread9 was denied the lock
// Thread8 exiting lock
// Thread10 acquired the lock
// Thread10 exiting lock

◆ exchange() [4/7]

static int64 xtd::threading::interlocked::exchange ( int64 location,
int64  value 
)
staticnoexcept

Sets a 64-bit signed integer to a specified value and returns the original value, as an atomic operation.

Parameters
locationThe variable to set to the specified value.
valueThe value to which the location parameter is set.
Returns
The original value of location.

◆ exchange() [5/7]

template<typename type_t >
static type_t xtd::threading::interlocked::exchange ( object location,
const object value 
)
inlinestaticnoexcept

Sets an object to a specified value and returns the original value, as an atomic operation.

Parameters
locationThe variable to set to the specified value.
valueThe value to which the location parameter is set.
Returns
The original value of location.

◆ exchange() [6/7]

template<typename type_t >
static type_t xtd::threading::interlocked::exchange ( type_t &  location,
type_t  value 
)
inlinestatic

Sets a variable of the specified type type_t to a specified value and returns the original value, as an atomic operation.

Parameters
locationThe variable to set to the specified value.
valueThe value to which the location parameter is set.
Returns
The original value of location.

◆ exchange() [7/7]

static void * xtd::threading::interlocked::exchange ( void *&  location,
void *  value 
)
staticnoexcept

Sets a platform-specific handles or pointers to a specified value and returns the original value, as an atomic operation.

Parameters
locationThe variable to set to the specified value.
valueThe value to which the location parameter is set.
Returns
The original value of location.

◆ increment() [1/2]

static int32 xtd::threading::interlocked::increment ( int32 location)
staticnoexcept

Increments a specified variable and stores the result, as an atomic operation.

Parameters
locationThe variable whose value is to be incremented.
Returns
The incremented value.
Examples
The following code example shows a thread-safe way to increment and decrement an integer value. SafeInstanceCount will always be zero. However, UnsafeInstanceCount will not necessarily be zero because a race condition occurs between incrementing and decrementing the count. This effect is especially marked on a multiprocessor computer.
#include <xtd/threading/interlocked>
#include <xtd/threading/thread>
#include <xtd/console>
#include <xtd/startup>
#include <vector>
using namespace xtd;
using namespace xtd::threading;
namespace interlocked_decrement_example {
class my_interlocked_decrement_class {
private:
class count_class {
public:
inline static int unsafe_instance_count = 0;
inline static int safe_instance_count = 0;
count_class() {
unsafe_instance_count++;
interlocked::increment(safe_instance_count);
}
~count_class() {
unsafe_instance_count--;
interlocked::decrement(safe_instance_count);
}
};
public:
static auto main() {
auto thread1 = thread {thread_method};
auto thread2 = thread {thread_method};
thread1.start();
thread2.start();
thread1.join();
thread2.join();
console::write_line("unsafe_instance_count: {}\nsafe_instance_count: {}", count_class::unsafe_instance_count, count_class::safe_instance_count);
}
private:
static void thread_method() {
auto cc = count_class {};
// Create 100000 instances of count_class.
for (auto i = 0; i < 100000; ++i)
cc = count_class {};
}
};
}
startup_(interlocked_decrement_example::my_interlocked_decrement_class::main);
// This code produces the following output:
//
// unsafe_instance_count: 253
// safe_instance_count: 0

◆ increment() [2/2]

static int64 xtd::threading::interlocked::increment ( int64 location)
staticnoexcept

Increments a specified variable and stores the result, as an atomic operation.

Parameters
locationThe variable whose value is to be incremented.
Returns
The incremented value.

◆ memory_barrier()

static void xtd::threading::interlocked::memory_barrier ( )
staticnoexcept

Synchronizes memory access as follows: The processor that executes the current thread cannot reorder instructions in such a way that memory accesses before the call to xtd::threading::interlocked::memory_barrier execute after memory accesses that follow the call to xtd::threading::interlocked::memory_barrier.

Remarks
This method was added to the xtd::threading::interlocked class as a convenience; it's a wrapper for the xtd::threading::thread::memory_barrier method.
xtd::threading::interlocked::memory_barrier is required only on multiprocessor systems that have weak memory ordering (for example, a system that employs multiple Intel Itanium processors).
For most purposes, the lock_ statement, or the Monitor class provide easier ways to synchronize data.

◆ read()

static int64 xtd::threading::interlocked::read ( int64 location)
staticnoexcept

Returns a 64-bit value, loaded as an atomic operation.

Parameters
locationThe 64-bit value to be loaded.
Returns
The loaded value.

The documentation for this class was generated from the following file: